From d04e2839689c4291afb4beb9a1913bb38fac1cd1 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Sat, 30 Jan 2021 10:46:23 +0000 Subject: Introduce a better system to handle command line input --- run_tests.bash | 20 ++- 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() { echo " -q | --queries :" echo " path to a folder containing SPARQL query files to be" echo " executed against the ontology and data." + echo " -j | --jar :" + echo " path to the fat jar to be executed." echo " -p | --prefix :" echo " provides a folder to prefix to the output files." echo " Defaults to './results'." @@ -43,6 +45,7 @@ print_help() { ONTOLOGY="" DATA="" QUERIES="" +JAR="" PREFIX="./results" while [[ $# -gt 0 ]] @@ -72,6 +75,14 @@ do print_help && \ exit 2 ;; + -j|--jar) + shift + JAR="$1" + [ ! -r "$JAR" ] && \ + msg_error "Unable to read jar '$JAR'" && \ + print_help && \ + exit 2 + ;; -p|--prefix) shift PREFIX="$1" @@ -104,12 +115,17 @@ done print_help && \ exit 3 +[ -z "$JAR" ] && \ + msg_error "Use -j | --jar to provide a jar file" && \ + print_help && \ + exit 3 DATAS=`\ls $DATA/*` mkdir -p "$PREFIX" -for QUERY in "$QUERIES"/*.sparql +for QUERY in "$QUERIES"/query*.sparql do - sbt "run $QUERY $ONTOLOGY $DATAS" 2>&1 | tee "$PREFIX/answers_$(basename $QUERY .sparql).txt" + #sbt "run $QUERY $ONTOLOGY $DATAS" 2>&1 | tee "$PREFIX/answers_$(basename $QUERY .sparql).txt" + 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" done 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 import util.{Logger, RDFoxUtil, RSA} import sparql.ConjunctiveQuery +case class RSAOption[+T](opt: T) { + def get[T]: T = opt.asInstanceOf[T] +} + +object RSAConfig { + type Config = Map[Symbol, RSAOption[Any]] + + private implicit def toRSAOption[T](opt: T) = RSAOption[T](opt) + + /** Help message */ + private val help: String = """ + rsacomb - combined approach for CQ answering for RSA ontologies. + + USAGE + rsacomb [OPTIONS] [ ...] + + -h | -? | --help + print this help message + + --rsacheck-only + only perform the RSA check without performing any query answering. + + -q | --query + path to a file containing a single SPARQL query + + + file containing the ontology + + + one or more data files + + """ + + /** Default config values */ + private val default = Map( + 'rsacheckonly -> RSAOption[Boolean](false) + ) + + /** Utility to exit the program with a custom message on stderr. + * + * The program will exit with error code 1 after printing the help + * message. + * + * @param msg message printed to stderr. + */ + private def exit(msg: String): Nothing = { + System.err.println(msg) + System.err.println() + System.err.println(help) + sys.exit(1) + } + + /** Parse arguments with default options + * + * @param args arguments list + */ + def parse(args: List[String]): Config = parse(args, default) + + /** Parse arguments + * + * @param args arguments list + * @param config default configuration + */ + def parse(args: List[String], config: Config): Config = { + args match { + case flag @ ("-h" | "-?" | "--help") :: _ => { + println(help) + sys.exit(0) + } + case "--rsacheck-only" :: tail => + parse(tail, config ++ Map('rsacheckonly -> true)) + case flag @ ("-q" | "--query") :: _query :: tail => { + val query = new File(_query) + if (!query.isFile) + exit(s"'$query' is not a valid filename.") + parse(tail, config ++ Map('query -> query)) + } + case _ontology :: _data => { + val ontology = new File(_ontology) + val data = _data.map(new File(_)) + (ontology :: data) foreach { (file) => + if (!file.isFile) + exit(s"'$file' is not a valid filename.") + } + finalise(config ++ Map('ontology -> ontology, 'data -> data)) + } + case a => exit(s"Invalid sequence of arguments '${a.mkString(" ")}'.") + } + } + + /** Perform final checks on parsed options */ + private def finalise(config: Config): Config = { + // Query file is mandatory unless only the RSA check is required. + if (!config('rsacheckonly).get[Boolean] && !config.contains('query)) + exit(s"Query file was not provided.") + + config + } +} + /** Entry point of the program. * * The executable expects a SPARQL query and a non-empty sequence of @@ -24,72 +124,56 @@ import sparql.ConjunctiveQuery */ object RSAComb extends App { - val help: String = """ - rsacomb - combined approach for CQ answering for RSA ontologies. - - USAGE - rsacomb [...] - - where - - query: path to a file containing a single SPARQL query - - ontology: one or more ontology files - - """ - - if (args.length < 2) { - println(help) - sys.exit; - } - - val queryPath = new File(args(0)) - val ontoPaths = args.drop(1).map(new File(_)) - - if (!queryPath.isFile || !ontoPaths.forall(_.isFile)) { - println("The provided arguments are not regular files.\n\n") - println(help) - sys.exit; - } + val config = RSAConfig.parse(args.toList) - val ontology = RSAOntology(ontoPaths: _*) + val ontology = + RSAOntology(config('ontology).get[File], config('data).get[List[File]]: _*) if (ontology.isRSA) { Logger print "Ontology is RSA!" - val query = RDFoxUtil.loadQueryFromFile(queryPath.getAbsoluteFile) - - ConjunctiveQuery.parse(query) match { - case Some(query) => { - val answers = ontology ask query - Logger.print(s"$answers", Logger.QUIET) - Logger print s"Number of answers: ${answers.length} (${answers.lengthWithMultiplicity})" - - /* Additional DEBUG information */ - if (Logger.level >= Logger.DEBUG) { - /* Unfiltered rules */ - val unfiltered = ontology askUnfiltered query - unfiltered map { u => - Logger print s"Number of unfiltered answers: ${u.length} (${u.map(_._1).sum})." - - /* Spurious answers */ - val spurious = { - val variables = query.variables.length - val sp = RDFoxUtil.buildDescriptionQuery("SP", variables) - ontology.queryDataStore(query, sp, RSA.Prefixes) - } - spurious map { s => - Logger print s"Number of spurious answers: ${s.length} (${s.map(_._1).sum})" - - /* Spurious/unfiltered percentage */ - val perc = - if (u.length > 0) (s.length / u.length.toFloat) * 100 else 0 - Logger print s"Percentage of spurious answers: $perc%" - } - } + if (!config('rsacheckonly).get[Boolean]) { + val query = + RDFoxUtil.loadQueryFromFile(config('query).get[File].getAbsoluteFile) + + ConjunctiveQuery.parse(query) match { + case Some(query) => { + val answers = ontology ask query + Logger.print(s"$answers", Logger.QUIET) + Logger print s"Number of answers: ${answers.length} (${answers.lengthWithMultiplicity})" + + // /* Additional DEBUG information */ + // if (Logger.level >= Logger.DEBUG) { + // /* Unfiltered rules */ + // val unfiltered = ontology askUnfiltered query + // unfiltered map { u => + // Logger print s"Number of unfiltered answers: ${u.length} (${u.map(_._1).sum})." + + // /* Spurious answers */ + // val spurious = { + // val variables = query.variables.length + // val sp = RDFoxUtil.buildDescriptionQuery("SP", variables) + // ontology.queryDataStore(query, sp, RSA.Prefixes) + // } + // spurious map { s => + // Logger print s"Number of spurious answers: ${s.length} (${s.map(_._1).sum})" + + // /* Spurious/unfiltered percentage */ + // val perc = + // if (u.length > 0) (s.length / u.length.toFloat) * 100 else 0 + // Logger print s"Percentage of spurious answers: $perc%" + // } + // } + // } } + case None => + throw new RuntimeException("Submitted query is not conjunctive") } - case None => - throw new RuntimeException("Submitted query is not conjunctive") } + } else { + + Logger print "Ontology is not RSA!" + } } -- cgit v1.2.3