diff options
Diffstat (limited to 'src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala')
-rw-r--r-- | src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | 165 |
1 files changed, 43 insertions, 122 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 258c226..121c65f 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | |||
@@ -16,7 +16,8 @@ | |||
16 | 16 | ||
17 | package uk.ac.ox.cs.rsacomb | 17 | package uk.ac.ox.cs.rsacomb |
18 | 18 | ||
19 | import java.io.File | 19 | import java.io.{File, PrintWriter} |
20 | import java.nio.file.{Path, Paths, InvalidPathException} | ||
20 | import java.util.HashMap | 21 | import java.util.HashMap |
21 | import scala.collection.JavaConverters._ | 22 | import scala.collection.JavaConverters._ |
22 | import tech.oxfordsemantic.jrdfox.client.UpdateType | 23 | import tech.oxfordsemantic.jrdfox.client.UpdateType |
@@ -28,97 +29,7 @@ import sparql.ConjunctiveQuery | |||
28 | 29 | ||
29 | import uk.ac.ox.cs.rsacomb.ontology.Ontology | 30 | import uk.ac.ox.cs.rsacomb.ontology.Ontology |
30 | import uk.ac.ox.cs.rsacomb.converter.Normalizer | 31 | import uk.ac.ox.cs.rsacomb.converter.Normalizer |
31 | import uk.ac.ox.cs.rsacomb.approximation.LowerBound | 32 | import uk.ac.ox.cs.rsacomb.approximation.{Upperbound, Lowerbound} |
32 | |||
33 | case class RSAOption[+T](opt: T) { | ||
34 | def get[T]: T = opt.asInstanceOf[T] | ||
35 | } | ||
36 | |||
37 | object RSAConfig { | ||
38 | type Config = Map[Symbol, RSAOption[Any]] | ||
39 | |||
40 | private implicit def toRSAOption[T](opt: T) = RSAOption[T](opt) | ||
41 | |||
42 | /** Help message */ | ||
43 | private val help: String = """ | ||
44 | rsacomb - combined approach for CQ answering for RSA ontologies. | ||
45 | |||
46 | USAGE | ||
47 | rsacomb [OPTIONS] <ontology> [<data> ...] | ||
48 | |||
49 | -h | -? | --help | ||
50 | print this help message | ||
51 | |||
52 | -q <file> | --query <file> | ||
53 | path to a file containing a single SPARQL query. If no query | ||
54 | is provided, only the approximation to RSA will be performed. | ||
55 | |||
56 | <ontology> | ||
57 | file containing the ontology | ||
58 | |||
59 | <data> | ||
60 | one or more data files | ||
61 | |||
62 | """ | ||
63 | |||
64 | /** Default config values */ | ||
65 | private val default: Config = Map.empty | ||
66 | |||
67 | /** Utility to exit the program with a custom message on stderr. | ||
68 | * | ||
69 | * The program will exit with error code 1 after printing the help | ||
70 | * message. | ||
71 | * | ||
72 | * @param msg message printed to stderr. | ||
73 | */ | ||
74 | private def exit(msg: String): Nothing = { | ||
75 | System.err.println(msg) | ||
76 | System.err.println() | ||
77 | System.err.println(help) | ||
78 | sys.exit(1) | ||
79 | } | ||
80 | |||
81 | /** Parse arguments with default options | ||
82 | * | ||
83 | * @param args arguments list | ||
84 | * @return map of config options | ||
85 | */ | ||
86 | def parse(args: List[String]): Config = parse(args, default) | ||
87 | |||
88 | /** Parse arguments | ||
89 | * | ||
90 | * @param args arguments list | ||
91 | * @param config default configuration | ||
92 | * @return map of config options | ||
93 | */ | ||
94 | def parse(args: List[String], config: Config): Config = { | ||
95 | args match { | ||
96 | case flag @ ("-h" | "-?" | "--help") :: _ => { | ||
97 | println(help) | ||
98 | sys.exit(0) | ||
99 | } | ||
100 | case flag @ ("-q" | "--query") :: _query :: tail => { | ||
101 | val query = new File(_query) | ||
102 | if (!query.isFile) | ||
103 | exit(s"'$query' is not a valid filename.") | ||
104 | parse(tail, config ++ Map('query -> query)) | ||
105 | } | ||
106 | case _ontology :: _data => { | ||
107 | val ontology = new File(_ontology) | ||
108 | val data = _data.map(new File(_)) | ||
109 | (ontology :: data) foreach { (file) => | ||
110 | if (!file.isFile) | ||
111 | exit(s"'$file' is not a valid filename.") | ||
112 | } | ||
113 | finalise(config ++ Map('ontology -> ontology, 'data -> data)) | ||
114 | } | ||
115 | case a => exit(s"Invalid sequence of arguments '${a.mkString(" ")}'.") | ||
116 | } | ||
117 | } | ||
118 | |||
119 | /** Perform final checks on parsed options */ | ||
120 | private def finalise(config: Config): Config = config | ||
121 | } | ||
122 | 33 | ||
123 | /** Main entry point to the program */ | 34 | /** Main entry point to the program */ |
124 | object RSAComb extends App { | 35 | object RSAComb extends App { |
@@ -126,42 +37,52 @@ object RSAComb extends App { | |||
126 | /* Command-line options */ | 37 | /* Command-line options */ |
127 | val config = RSAConfig.parse(args.toList) | 38 | val config = RSAConfig.parse(args.toList) |
128 | 39 | ||
40 | /* Set logger level */ | ||
41 | if (config.contains('logger)) | ||
42 | Logger.level = config('logger).get[Logger.Level] | ||
43 | |||
129 | /* Load original ontology and normalize it */ | 44 | /* Load original ontology and normalize it */ |
130 | val ontology = Ontology( | 45 | val ontology = Ontology( |
131 | config('ontology).get[File], | 46 | config('ontology).get[os.Path], |
132 | config('data).get[List[File]] | 47 | config('data).get[List[os.Path]] |
133 | ).normalize(new Normalizer) | 48 | ).normalize(new Normalizer) |
134 | 49 | ||
50 | //ontology.axioms foreach println | ||
51 | |||
135 | /* Approximate the ontology to RSA */ | 52 | /* Approximate the ontology to RSA */ |
136 | val toRSA = new LowerBound | 53 | val toRSA = new Upperbound |
137 | val rsa = ontology approximate toRSA | 54 | val rsa = ontology approximate toRSA |
138 | 55 | ||
139 | if (config contains 'query) { | 56 | if (config contains 'queries) { |
140 | val query = | 57 | val queries = |
141 | RDFoxUtil.loadQueryFromFile(config('query).get[File].getAbsoluteFile) | 58 | RDFoxUtil.loadQueriesFromFile( |
142 | 59 | config('queries).get[os.Path] | |
143 | ConjunctiveQuery.parse(query) match { | 60 | ) |
144 | case Some(query) => { | 61 | |
145 | // Retrieve answers | 62 | val answers = rsa ask queries |
146 | val answers = rsa ask query | 63 | |
147 | Logger.print(s"$answers", Logger.VERBOSE) | 64 | /* Write answers to output file */ |
148 | Logger print s"Number of answers: ${answers.length} (${answers.lengthWithMultiplicity})" | 65 | if (config.contains('answers)) |
149 | // Retrieve unfiltered answers | 66 | os.write( |
150 | // val unfiltered = rsa.queryDataStore( | 67 | config('answers).get[os.Path], |
151 | // """ | 68 | ujson.write(ujson.Arr(answers.map(_.toJSON)), indent = 4), |
152 | // SELECT (count(?K) as ?COUNT) | 69 | createFolders = true |
153 | // WHERE { | 70 | ) |
154 | // ?K a rsa:QM . | 71 | |
155 | // } | 72 | // Logger.print(s"$answers", Logger.VERBOSE) |
156 | // """, | 73 | // Logger print s"Number of answers: ${answers.length} (${answers.lengthWithMultiplicity})" |
157 | // RSA.Prefixes | 74 | // Retrieve unfiltered answers |
158 | // ) | 75 | // val unfiltered = rsa.queryDataStore( |
159 | // unfiltered.foreach((u) => | 76 | // """ |
160 | // Logger print s"Number of unfiltered answers: ${u.head._2}" | 77 | // SELECT (count(?K) as ?COUNT) |
161 | // ) | 78 | // WHERE { |
162 | } | 79 | // ?K a rsa:QM . |
163 | case None => | 80 | // } |
164 | throw new RuntimeException("Submitted query is not conjunctive") | 81 | // """, |
165 | } | 82 | // RSA.Prefixes |
83 | // ) | ||
84 | // unfiltered.foreach((u) => | ||
85 | // Logger print s"Number of unfiltered answers: ${u.head._2}" | ||
86 | // ) | ||
166 | } | 87 | } |
167 | } | 88 | } |