diff options
Diffstat (limited to 'src')
| -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 | } |
