diff options
Diffstat (limited to 'src/main/scala/uk/ac/ox')
-rw-r--r-- | src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | 200 |
1 files changed, 142 insertions, 58 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 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 | } |