diff options
author | Federico Igne <federico.igne@cs.ox.ac.uk> | 2021-01-30 10:46:23 +0000 |
---|---|---|
committer | Federico Igne <federico.igne@cs.ox.ac.uk> | 2021-01-30 10:48:29 +0000 |
commit | d04e2839689c4291afb4beb9a1913bb38fac1cd1 (patch) | |
tree | 365e5ea2ef138e83ff46ecce72c716de6686e78e | |
parent | 87b180f1de0e1c30a4624e546825b77c2edf9bbe (diff) | |
download | RSAComb-d04e2839689c4291afb4beb9a1913bb38fac1cd1.tar.gz RSAComb-d04e2839689c4291afb4beb9a1913bb38fac1cd1.zip |
Introduce a better system to handle command line input
-rwxr-xr-x | run_tests.bash | 20 | ||||
-rw-r--r-- | src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | 200 |
2 files changed, 160 insertions, 60 deletions
diff --git a/run_tests.bash b/run_tests.bash index 17cbf01..92e0223 100755 --- a/run_tests.bash +++ b/run_tests.bash | |||
@@ -32,6 +32,8 @@ print_help() { | |||
32 | echo " -q | --queries <path>:" | 32 | echo " -q | --queries <path>:" |
33 | echo " path to a folder containing SPARQL query files to be" | 33 | echo " path to a folder containing SPARQL query files to be" |
34 | echo " executed against the ontology and data." | 34 | echo " executed against the ontology and data." |
35 | echo " -j | --jar <path>:" | ||
36 | echo " path to the fat jar to be executed." | ||
35 | echo " -p | --prefix <path>:" | 37 | echo " -p | --prefix <path>:" |
36 | echo " provides a folder to prefix to the output files." | 38 | echo " provides a folder to prefix to the output files." |
37 | echo " Defaults to './results'." | 39 | echo " Defaults to './results'." |
@@ -43,6 +45,7 @@ print_help() { | |||
43 | ONTOLOGY="" | 45 | ONTOLOGY="" |
44 | DATA="" | 46 | DATA="" |
45 | QUERIES="" | 47 | QUERIES="" |
48 | JAR="" | ||
46 | PREFIX="./results" | 49 | PREFIX="./results" |
47 | 50 | ||
48 | while [[ $# -gt 0 ]] | 51 | while [[ $# -gt 0 ]] |
@@ -72,6 +75,14 @@ do | |||
72 | print_help && \ | 75 | print_help && \ |
73 | exit 2 | 76 | exit 2 |
74 | ;; | 77 | ;; |
78 | -j|--jar) | ||
79 | shift | ||
80 | JAR="$1" | ||
81 | [ ! -r "$JAR" ] && \ | ||
82 | msg_error "Unable to read jar '$JAR'" && \ | ||
83 | print_help && \ | ||
84 | exit 2 | ||
85 | ;; | ||
75 | -p|--prefix) | 86 | -p|--prefix) |
76 | shift | 87 | shift |
77 | PREFIX="$1" | 88 | PREFIX="$1" |
@@ -104,12 +115,17 @@ done | |||
104 | print_help && \ | 115 | print_help && \ |
105 | exit 3 | 116 | exit 3 |
106 | 117 | ||
118 | [ -z "$JAR" ] && \ | ||
119 | msg_error "Use -j | --jar to provide a jar file" && \ | ||
120 | print_help && \ | ||
121 | exit 3 | ||
107 | 122 | ||
108 | DATAS=`\ls $DATA/*` | 123 | DATAS=`\ls $DATA/*` |
109 | mkdir -p "$PREFIX" | 124 | mkdir -p "$PREFIX" |
110 | for QUERY in "$QUERIES"/*.sparql | 125 | for QUERY in "$QUERIES"/query*.sparql |
111 | do | 126 | do |
112 | sbt "run $QUERY $ONTOLOGY $DATAS" 2>&1 | tee "$PREFIX/answers_$(basename $QUERY .sparql).txt" | 127 | #sbt "run $QUERY $ONTOLOGY $DATAS" 2>&1 | tee "$PREFIX/answers_$(basename $QUERY .sparql).txt" |
128 | java -cp ./lib/JRDFox.jar:"$JAR" uk.ac.ox.cs.rsacomb.RSAComb -q "$QUERY" "$ONTOLOGY" "$DATA"/* 2>&1 | tee "$PREFIX/answers_$(basename $QUERY .sparql).txt" | ||
113 | done | 129 | done |
114 | 130 | ||
115 | OUTPUT="$PREFIX/results.csv" | 131 | OUTPUT="$PREFIX/results.csv" |
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 bf96a31..909cfdd 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | |||
@@ -10,6 +10,106 @@ import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery | |||
10 | import util.{Logger, RDFoxUtil, RSA} | 10 | import util.{Logger, RDFoxUtil, RSA} |
11 | import sparql.ConjunctiveQuery | 11 | import sparql.ConjunctiveQuery |
12 | 12 | ||
13 | case class RSAOption[+T](opt: T) { | ||
14 | def get[T]: T = opt.asInstanceOf[T] | ||
15 | } | ||
16 | |||
17 | object RSAConfig { | ||
18 | type Config = Map[Symbol, RSAOption[Any]] | ||
19 | |||
20 | private implicit def toRSAOption[T](opt: T) = RSAOption[T](opt) | ||
21 | |||
22 | /** Help message */ | ||
23 | private val help: String = """ | ||
24 | rsacomb - combined approach for CQ answering for RSA ontologies. | ||
25 | |||
26 | USAGE | ||
27 | rsacomb [OPTIONS] <ontology> [<data> ...] | ||
28 | |||
29 | -h | -? | --help | ||
30 | print this help message | ||
31 | |||
32 | --rsacheck-only | ||
33 | only perform the RSA check without performing any query answering. | ||
34 | |||
35 | -q <file> | --query <file> | ||
36 | path to a file containing a single SPARQL query | ||
37 | |||
38 | <ontology> | ||
39 | file containing the ontology | ||
40 | |||
41 | <data> | ||
42 | one or more data files | ||
43 | |||
44 | """ | ||
45 | |||
46 | /** Default config values */ | ||
47 | private val default = Map( | ||
48 | 'rsacheckonly -> RSAOption[Boolean](false) | ||
49 | ) | ||
50 | |||
51 | /** Utility to exit the program with a custom message on stderr. | ||
52 | * | ||
53 | * The program will exit with error code 1 after printing the help | ||
54 | * message. | ||
55 | * | ||
56 | * @param msg message printed to stderr. | ||
57 | */ | ||
58 | private def exit(msg: String): Nothing = { | ||
59 | System.err.println(msg) | ||
60 | System.err.println() | ||
61 | System.err.println(help) | ||
62 | sys.exit(1) | ||
63 | } | ||
64 | |||
65 | /** Parse arguments with default options | ||
66 | * | ||
67 | * @param args arguments list | ||
68 | */ | ||
69 | def parse(args: List[String]): Config = parse(args, default) | ||
70 | |||
71 | /** Parse arguments | ||
72 | * | ||
73 | * @param args arguments list | ||
74 | * @param config default configuration | ||
75 | */ | ||
76 | def parse(args: List[String], config: Config): Config = { | ||
77 | args match { | ||
78 | case flag @ ("-h" | "-?" | "--help") :: _ => { | ||
79 | println(help) | ||
80 | sys.exit(0) | ||
81 | } | ||
82 | case "--rsacheck-only" :: tail => | ||
83 | parse(tail, config ++ Map('rsacheckonly -> true)) | ||
84 | case flag @ ("-q" | "--query") :: _query :: tail => { | ||
85 | val query = new File(_query) | ||
86 | if (!query.isFile) | ||
87 | exit(s"'$query' is not a valid filename.") | ||
88 | parse(tail, config ++ Map('query -> query)) | ||
89 | } | ||
90 | case _ontology :: _data => { | ||
91 | val ontology = new File(_ontology) | ||
92 | val data = _data.map(new File(_)) | ||
93 | (ontology :: data) foreach { (file) => | ||
94 | if (!file.isFile) | ||
95 | exit(s"'$file' is not a valid filename.") | ||
96 | } | ||
97 | finalise(config ++ Map('ontology -> ontology, 'data -> data)) | ||
98 | } | ||
99 | case a => exit(s"Invalid sequence of arguments '${a.mkString(" ")}'.") | ||
100 | } | ||
101 | } | ||
102 | |||
103 | /** Perform final checks on parsed options */ | ||
104 | private def finalise(config: Config): Config = { | ||
105 | // Query file is mandatory unless only the RSA check is required. | ||
106 | if (!config('rsacheckonly).get[Boolean] && !config.contains('query)) | ||
107 | exit(s"Query file was not provided.") | ||
108 | |||
109 | config | ||
110 | } | ||
111 | } | ||
112 | |||
13 | /** Entry point of the program. | 113 | /** Entry point of the program. |
14 | * | 114 | * |
15 | * The executable expects a SPARQL query and a non-empty sequence of | 115 | * The executable expects a SPARQL query and a non-empty sequence of |
@@ -24,72 +124,56 @@ import sparql.ConjunctiveQuery | |||
24 | */ | 124 | */ |
25 | object RSAComb extends App { | 125 | object RSAComb extends App { |
26 | 126 | ||
27 | val help: String = """ | 127 | val config = RSAConfig.parse(args.toList) |
28 | rsacomb - combined approach for CQ answering for RSA ontologies. | ||
29 | |||
30 | USAGE | ||
31 | rsacomb <query> <ontology> [...] | ||
32 | |||
33 | where | ||
34 | - query: path to a file containing a single SPARQL query | ||
35 | - ontology: one or more ontology files | ||
36 | |||
37 | """ | ||
38 | |||
39 | if (args.length < 2) { | ||
40 | println(help) | ||
41 | sys.exit; | ||
42 | } | ||
43 | |||
44 | val queryPath = new File(args(0)) | ||
45 | val ontoPaths = args.drop(1).map(new File(_)) | ||
46 | |||
47 | if (!queryPath.isFile || !ontoPaths.forall(_.isFile)) { | ||
48 | println("The provided arguments are not regular files.\n\n") | ||
49 | println(help) | ||
50 | sys.exit; | ||
51 | } | ||
52 | 128 | ||
53 | val ontology = RSAOntology(ontoPaths: _*) | 129 | val ontology = |
130 | RSAOntology(config('ontology).get[File], config('data).get[List[File]]: _*) | ||
54 | if (ontology.isRSA) { | 131 | if (ontology.isRSA) { |
55 | 132 | ||
56 | Logger print "Ontology is RSA!" | 133 | Logger print "Ontology is RSA!" |
57 | 134 | ||
58 | val query = RDFoxUtil.loadQueryFromFile(queryPath.getAbsoluteFile) | 135 | if (!config('rsacheckonly).get[Boolean]) { |
59 | 136 | val query = | |
60 | ConjunctiveQuery.parse(query) match { | 137 | RDFoxUtil.loadQueryFromFile(config('query).get[File].getAbsoluteFile) |
61 | case Some(query) => { | 138 | |
62 | val answers = ontology ask query | 139 | ConjunctiveQuery.parse(query) match { |
63 | Logger.print(s"$answers", Logger.QUIET) | 140 | case Some(query) => { |
64 | Logger print s"Number of answers: ${answers.length} (${answers.lengthWithMultiplicity})" | 141 | val answers = ontology ask query |
65 | 142 | Logger.print(s"$answers", Logger.QUIET) | |
66 | /* Additional DEBUG information */ | 143 | Logger print s"Number of answers: ${answers.length} (${answers.lengthWithMultiplicity})" |
67 | if (Logger.level >= Logger.DEBUG) { | 144 | |
68 | /* Unfiltered rules */ | 145 | // /* Additional DEBUG information */ |
69 | val unfiltered = ontology askUnfiltered query | 146 | // if (Logger.level >= Logger.DEBUG) { |
70 | unfiltered map { u => | 147 | // /* Unfiltered rules */ |
71 | Logger print s"Number of unfiltered answers: ${u.length} (${u.map(_._1).sum})." | 148 | // val unfiltered = ontology askUnfiltered query |
72 | 149 | // unfiltered map { u => | |
73 | /* Spurious answers */ | 150 | // Logger print s"Number of unfiltered answers: ${u.length} (${u.map(_._1).sum})." |
74 | val spurious = { | 151 | |
75 | val variables = query.variables.length | 152 | // /* Spurious answers */ |
76 | val sp = RDFoxUtil.buildDescriptionQuery("SP", variables) | 153 | // val spurious = { |
77 | ontology.queryDataStore(query, sp, RSA.Prefixes) | 154 | // val variables = query.variables.length |
78 | } | 155 | // val sp = RDFoxUtil.buildDescriptionQuery("SP", variables) |
79 | spurious map { s => | 156 | // ontology.queryDataStore(query, sp, RSA.Prefixes) |
80 | Logger print s"Number of spurious answers: ${s.length} (${s.map(_._1).sum})" | 157 | // } |
81 | 158 | // spurious map { s => | |
82 | /* Spurious/unfiltered percentage */ | 159 | // Logger print s"Number of spurious answers: ${s.length} (${s.map(_._1).sum})" |
83 | val perc = | 160 | |
84 | if (u.length > 0) (s.length / u.length.toFloat) * 100 else 0 | 161 | // /* Spurious/unfiltered percentage */ |
85 | Logger print s"Percentage of spurious answers: $perc%" | 162 | // val perc = |
86 | } | 163 | // if (u.length > 0) (s.length / u.length.toFloat) * 100 else 0 |
87 | } | 164 | // Logger print s"Percentage of spurious answers: $perc%" |
165 | // } | ||
166 | // } | ||
167 | // } | ||
88 | } | 168 | } |
169 | case None => | ||
170 | throw new RuntimeException("Submitted query is not conjunctive") | ||
89 | } | 171 | } |
90 | case None => | ||
91 | throw new RuntimeException("Submitted query is not conjunctive") | ||
92 | } | 172 | } |
93 | 173 | ||
174 | } else { | ||
175 | |||
176 | Logger print "Ontology is not RSA!" | ||
177 | |||
94 | } | 178 | } |
95 | } | 179 | } |