aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFederico Igne <git@federicoigne.com>2021-10-03 22:09:32 +0100
committerFederico Igne <git@federicoigne.com>2021-10-03 22:09:32 +0100
commit830b8d9f8902a4a3fc536f40843352e9ff905020 (patch)
tree0ab95df791f1ce82c5b9f96a36ff7ec39dbe5f41
parentc45035e9e50d92e85b851724466a950fc1788a02 (diff)
downloadRSAComb-830b8d9f8902a4a3fc536f40843352e9ff905020.tar.gz
RSAComb-830b8d9f8902a4a3fc536f40843352e9ff905020.zip
Rework CLI configs
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala134
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/RSAConfig.scala154
2 files changed, 164 insertions, 124 deletions
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala
index 24bda1f..121c65f 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala
@@ -19,7 +19,6 @@ package uk.ac.ox.cs.rsacomb
19import java.io.{File, PrintWriter} 19import java.io.{File, PrintWriter}
20import java.nio.file.{Path, Paths, InvalidPathException} 20import java.nio.file.{Path, Paths, InvalidPathException}
21import java.util.HashMap 21import java.util.HashMap
22import scala.collection.mutable.Map
23import scala.collection.JavaConverters._ 22import scala.collection.JavaConverters._
24import tech.oxfordsemantic.jrdfox.client.UpdateType 23import tech.oxfordsemantic.jrdfox.client.UpdateType
25import tech.oxfordsemantic.jrdfox.logic.expression.{IRI, Term} 24import tech.oxfordsemantic.jrdfox.logic.expression.{IRI, Term}
@@ -32,129 +31,15 @@ import uk.ac.ox.cs.rsacomb.ontology.Ontology
32import uk.ac.ox.cs.rsacomb.converter.Normalizer 31import uk.ac.ox.cs.rsacomb.converter.Normalizer
33import uk.ac.ox.cs.rsacomb.approximation.{Upperbound, Lowerbound} 32import uk.ac.ox.cs.rsacomb.approximation.{Upperbound, Lowerbound}
34 33
35case class RSAOption[+T](opt: T) {
36 def get[T]: T = opt.asInstanceOf[T]
37}
38
39object RSAConfig {
40 type Config = Map[Symbol, RSAOption[Any]]
41
42 private implicit def toRSAOption[T](opt: T) = RSAOption[T](opt)
43
44 /** Help message */
45 private val help: String = """
46 rsacomb - combined approach for CQ answering for RSA ontologies.
47
48 USAGE
49 rsacomb [OPTIONS] <ontology> [<data> ...]
50
51 -h | -? | --help
52 print this help message
53
54 -l | --logger <level>
55 specify the logger verbosity. Values are: quiet, normal (default),
56 debug, verbose.
57
58 -o | --output <file>
59 path to the output file for the answers to the query (in JSON
60 format)
61
62 -q | --queries <file>
63 path to a file containing a single SPARQL query. If no query
64 is provided, only the approximation to RSA will be performed.
65
66 <ontology>
67 file containing the ontology
68
69 <data>
70 one or more data files
71
72 """
73
74 /** Default config values */
75 private val default: Config = Map.empty
76
77 /** Utility to exit the program with a custom message on stderr.
78 *
79 * The program will exit with error code 1 after printing the help
80 * message.
81 *
82 * @param msg message printed to stderr.
83 */
84 private def exit(msg: String): Nothing = {
85 System.err.println(msg)
86 System.err.println()
87 System.err.println(help)
88 sys.exit(1)
89 }
90
91 private def getPath(str: String): os.Path =
92 try {
93 os.Path(str, base = os.pwd)
94 } catch {
95 case e: IllegalArgumentException =>
96 exit(s"'$str' is not a well formed path.")
97 }
98
99 /** Parse arguments with default options
100 *
101 * @param args arguments list
102 * @return map of config options
103 */
104 def parse(args: List[String]): Config = parse(args, default)
105
106 /** Parse arguments
107 *
108 * @param args arguments list
109 * @param config default configuration
110 * @return map of config options
111 */
112 def parse(args: List[String], config: Config): Config = {
113 args match {
114 case flag @ ("-h" | "-?" | "--help") :: _ => {
115 println(help)
116 sys.exit(0)
117 }
118 case flag @ ("-l" | "--logger") :: _level :: tail => {
119 val level = _level match {
120 case "quiet" => Logger.QUIET
121 case "debug" => Logger.DEBUG
122 case "verbose" => Logger.VERBOSE
123 case _ => Logger.NORMAL
124 }
125 parse(tail, config += ('logger -> level))
126 }
127 case flag @ ("-o" | "--output") :: output :: tail =>
128 parse(tail, config += ('output -> getPath(output)))
129 case flag @ ("-q" | "--queries") :: _query :: tail => {
130 val query = getPath(_query)
131 if (!os.isFile(query))
132 exit(s"'${_query}' is not a valid filename.")
133 parse(tail, config += ('queries -> query))
134 }
135 case _ontology :: _data => {
136 val ontology = getPath(_ontology)
137 val data = _data map getPath
138 (ontology :: data) foreach { (path) =>
139 if (!os.isFile(path))
140 exit(s"'$path' is not a valid filename.")
141 }
142 finalise(config += ('ontology -> ontology) += ('data -> data))
143 }
144 case a => exit(s"Invalid sequence of arguments '${a.mkString(" ")}'.")
145 }
146 }
147
148 /** Perform final checks on parsed options */
149 private def finalise(config: Config): Config = config
150}
151
152/** Main entry point to the program */ 34/** Main entry point to the program */
153object RSAComb extends App { 35object RSAComb extends App {
154 36
155 /* Command-line options */ 37 /* Command-line options */
156 val config = RSAConfig.parse(args.toList) 38 val config = RSAConfig.parse(args.toList)
157 Logger.level = config('logger).get[Logger.Level] 39
40 /* Set logger level */
41 if (config.contains('logger))
42 Logger.level = config('logger).get[Logger.Level]
158 43
159 /* Load original ontology and normalize it */ 44 /* Load original ontology and normalize it */
160 val ontology = Ontology( 45 val ontology = Ontology(
@@ -177,11 +62,12 @@ object RSAComb extends App {
177 val answers = rsa ask queries 62 val answers = rsa ask queries
178 63
179 /* Write answers to output file */ 64 /* Write answers to output file */
180 os.write( 65 if (config.contains('answers))
181 config('output).get[os.Path], 66 os.write(
182 ujson.write(ujson.Arr(answers.map(_.toJSON)), indent = 4), 67 config('answers).get[os.Path],
183 createFolders = true 68 ujson.write(ujson.Arr(answers.map(_.toJSON)), indent = 4),
184 ) 69 createFolders = true
70 )
185 71
186 // Logger.print(s"$answers", Logger.VERBOSE) 72 // Logger.print(s"$answers", Logger.VERBOSE)
187 // Logger print s"Number of answers: ${answers.length} (${answers.lengthWithMultiplicity})" 73 // Logger print s"Number of answers: ${answers.length} (${answers.lengthWithMultiplicity})"
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAConfig.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAConfig.scala
new file mode 100644
index 0000000..4d96850
--- /dev/null
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAConfig.scala
@@ -0,0 +1,154 @@
1/*
2 * Copyright 2020, 2021 KRR Oxford
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package uk.ac.ox.cs.rsacomb
18
19import scala.collection.mutable.Map
20import util.Logger
21
22case class RSAOption[+T](opt: T) {
23 def get[T]: T = opt.asInstanceOf[T]
24}
25
26object RSAConfig {
27 type Config = Map[Symbol, RSAOption[Any]]
28
29 private implicit def toRSAOption[T](opt: T) = RSAOption[T](opt)
30
31 /** Help message */
32 private val help: String = """
33 rsacomb - combined approach for CQ answering for RSA ontologies.
34
35 USAGE
36 rsacomb [OPTIONS] <ontology> [<data> ...]
37
38 -h | -? | --help
39 print this help message
40
41 -l | --logger <level>
42 specify the logger verbosity. Values are: quiet, normal (default),
43 debug, verbose.
44
45 -a | --answers <file>
46 path to the output file for the answers to the query (in JSON
47 format)
48
49 -q | --queries <file>
50 path to a file containing a single SPARQL query. If no query
51 is provided, only the approximation to RSA will be performed.
52
53 -o | --ontology <file>
54 ontology file in OWL format.
55
56 -d | --data <file>
57 data files to be used alongside the ontology file. If a
58 directory is provided, all files in the directory (recursively)
59 will be considered.
60
61 """
62
63 /** Default config values */
64 private val default: Config = Map.empty
65
66 /** Utility to exit the program with a custom message on stderr.
67 *
68 * The program will exit with error code 1 after printing the help
69 * message.
70 *
71 * @param msg message printed to stderr.
72 */
73 private def exit(msg: String): Nothing = {
74 System.err.println(msg)
75 System.err.println()
76 System.err.println(help)
77 sys.exit(1)
78 }
79
80 private def getPath(str: String): os.Path =
81 try {
82 os.Path(str, base = os.pwd)
83 } catch {
84 case e: IllegalArgumentException =>
85 exit(s"'$str' is not a well formed path.")
86 }
87
88 /** Parse arguments with default options
89 *
90 * @param args arguments list
91 * @return map of config options
92 */
93 def parse(args: List[String]): Config = parse(args, default)
94
95 /** Parse arguments
96 *
97 * @param args arguments list
98 * @param config default configuration
99 * @return map of config options
100 */
101 def parse(args: List[String], config: Config): Config = {
102 args match {
103 case Nil => finalise(config)
104 case flag @ ("-h" | "-?" | "--help") :: _ => {
105 println(help)
106 sys.exit(0)
107 }
108 case flag @ ("-l" | "--logger") :: _level :: tail => {
109 val level = _level match {
110 case "quiet" => Logger.QUIET
111 case "debug" => Logger.DEBUG
112 case "verbose" => Logger.VERBOSE
113 case _ => Logger.NORMAL
114 }
115 parse(tail, config += ('logger -> level))
116 }
117 case flag @ ("-a" | "--answers") :: answers :: tail =>
118 parse(tail, config += ('answers -> getPath(answers)))
119 case flag @ ("-q" | "--queries") :: _query :: tail => {
120 val query = getPath(_query)
121 if (!os.isFile(query))
122 exit(s"'${_query}' is not a valid filename.")
123 parse(tail, config += ('queries -> query))
124 }
125 case flag @ ("-o" | "--ontology") :: _ontology :: tail => {
126 val ontology = getPath(_ontology)
127 if (!os.isFile(ontology))
128 exit(s"'${_ontology}' is not a valid filename.")
129 parse(tail, config += ('ontology -> ontology))
130 }
131 case flag @ ("-d" | "--data") :: _data :: tail => {
132 val data = getPath(_data)
133 val files =
134 if (os.isFile(data))
135 Seq(data)
136 else if (os.isDir(data))
137 os.walk(data).filter(os.isFile)
138 else
139 exit(s"'${_data}' is not a valid path.")
140 parse(tail, config += ('data -> files))
141 }
142 case a => exit(s"Invalid sequence of arguments '${a.mkString(" ")}'.")
143 }
144 }
145
146 /** Perform final checks on parsed options */
147 private def finalise(config: Config): Config = {
148 if (!config.contains('ontology))
149 exit("The following flag is mandatory: '-o' or '--ontology'.")
150 if (!config.contains('data))
151 config += ('data -> List.empty[os.Path])
152 config
153 }
154}