diff options
| author | Federico Igne <federico.igne@cs.ox.ac.uk> | 2022-05-11 12:35:56 +0100 |
|---|---|---|
| committer | Federico Igne <federico.igne@cs.ox.ac.uk> | 2022-05-11 12:35:56 +0100 |
| commit | 32fe8f94255b913c570275043a3c056eaa4ec07b (patch) | |
| tree | 2a370c3998db1eab758f214f16f2cebc3fd2ef34 /src/main/scala/uk/ac/ox/cs/acqua/util | |
| parent | eaa8ed73aec7ca0ba02eb3e1c954388b8dbfc2cd (diff) | |
| download | ACQuA-32fe8f94255b913c570275043a3c056eaa4ec07b.tar.gz ACQuA-32fe8f94255b913c570275043a3c056eaa4ec07b.zip | |
Implement stub for query answering procedure
Diffstat (limited to 'src/main/scala/uk/ac/ox/cs/acqua/util')
| -rw-r--r-- | src/main/scala/uk/ac/ox/cs/acqua/util/AcquaConfig.scala | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/src/main/scala/uk/ac/ox/cs/acqua/util/AcquaConfig.scala b/src/main/scala/uk/ac/ox/cs/acqua/util/AcquaConfig.scala new file mode 100644 index 0000000..675a592 --- /dev/null +++ b/src/main/scala/uk/ac/ox/cs/acqua/util/AcquaConfig.scala | |||
| @@ -0,0 +1,211 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2021,2022 KRR Oxford | ||
| 3 | * | ||
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | * you may not use this file except in compliance with the License. | ||
| 6 | * You may obtain a copy of the License at | ||
| 7 | * | ||
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | * | ||
| 10 | * Unless required by applicable law or agreed to in writing, software | ||
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | * See the License for the specific language governing permissions and | ||
| 14 | * limitations under the License. | ||
| 15 | */ | ||
| 16 | |||
| 17 | package uk.ac.ox.cs.acqua.util | ||
| 18 | |||
| 19 | import scala.collection.mutable.Map | ||
| 20 | |||
| 21 | //import util.Logger | ||
| 22 | |||
| 23 | case class AcquaOption[+T](opt: T) { | ||
| 24 | def get[T]: T = opt.asInstanceOf[T] | ||
| 25 | } | ||
| 26 | |||
| 27 | object AcquaConfig { | ||
| 28 | type Config = Map[Symbol, AcquaOption[Any]] | ||
| 29 | |||
| 30 | private implicit def toRSAOption[T](opt: T) = AcquaOption[T](opt) | ||
| 31 | |||
| 32 | /** Help message */ | ||
| 33 | private val help: String = """ | ||
| 34 | |||
| 35 | rsacomb - combined approach for CQ answering for RSA ontologies. | ||
| 36 | |||
| 37 | USAGE | ||
| 38 | rsacomb [OPTIONS] <ontology> [<data> ...] | ||
| 39 | |||
| 40 | -h | -? | --help | ||
| 41 | print this help message | ||
| 42 | |||
| 43 | -l | --logger <level> | ||
| 44 | specify the logger verbosity. Values are: quiet, normal (default), | ||
| 45 | debug, verbose. | ||
| 46 | |||
| 47 | -a | --answers <file> | ||
| 48 | path to the output file for the answers to the query (in JSON | ||
| 49 | format) | ||
| 50 | |||
| 51 | -q | --queries <file> | ||
| 52 | path to a file containing a single SPARQL query. If no query | ||
| 53 | is provided, only the approximation to RSA will be performed. | ||
| 54 | |||
| 55 | -o | --ontology <file> | ||
| 56 | ontology file in OWL format. | ||
| 57 | |||
| 58 | -d | --data <file> | ||
| 59 | data files to be used alongside the ontology file. If a | ||
| 60 | directory is provided, all files in the directory (recursively) | ||
| 61 | will be considered. | ||
| 62 | |||
| 63 | -x | --approximation <string> | ||
| 64 | values available are "lowerupper" or "upperbound" corresponding | ||
| 65 | to the two algorithms for ontology approximation shipping by | ||
| 66 | default with RSAComb. You will need to change the source code to | ||
| 67 | expose custom approximation modules through the CLI. | ||
| 68 | |||
| 69 | -t | --transitive | ||
| 70 | "upperbound" approximation specific option. Include property chain | ||
| 71 | axioms (and hence the more common transitive properties) when | ||
| 72 | computing the canonical model. | ||
| 73 | |||
| 74 | """ | ||
| 75 | |||
| 76 | /** Default config values */ | ||
| 77 | private val default: Config = Map( | ||
| 78 | 'transitive -> false, | ||
| 79 | 'data -> List.empty[os.Path], | ||
| 80 | 'approximation -> 'lowerbound | ||
| 81 | ) | ||
| 82 | |||
| 83 | /** Parse a string into a path. | ||
| 84 | * | ||
| 85 | * @throws an [[IllegalArgumentException]] on malformed path. | ||
| 86 | */ | ||
| 87 | private def getPath(str: String): os.Path = | ||
| 88 | try { | ||
| 89 | os.Path(str, base = os.pwd) | ||
| 90 | } catch { | ||
| 91 | case e: IllegalArgumentException => | ||
| 92 | exit(s"'$str' is not a well formed path.") | ||
| 93 | } | ||
| 94 | |||
| 95 | /** Utility to exit the program with a custom message on stderr. | ||
| 96 | * | ||
| 97 | * The program will exit with error after printing the help message. | ||
| 98 | * | ||
| 99 | * @param msg message printed to stderr. | ||
| 100 | * @param errno error code number (defaults to 1) | ||
| 101 | */ | ||
| 102 | private def exit(msg: String, errno: Int = 1): Nothing = { | ||
| 103 | System.err.println(msg) | ||
| 104 | System.err.println(help) | ||
| 105 | sys.exit(errno) | ||
| 106 | } | ||
| 107 | |||
| 108 | /** Parse arguments with default options. | ||
| 109 | * | ||
| 110 | * @param args arguments list | ||
| 111 | * @return map of config options | ||
| 112 | */ | ||
| 113 | def parse(args: List[String]): Config = parse(args, default) | ||
| 114 | |||
| 115 | /** Parse arguments. | ||
| 116 | * | ||
| 117 | * @param args arguments list | ||
| 118 | * @param config default configuration | ||
| 119 | * @return map of config options | ||
| 120 | */ | ||
| 121 | def parse(args: List[String], config: Config): Config = { | ||
| 122 | args match { | ||
| 123 | case Nil => finalise(config) | ||
| 124 | case flag @ ("-h" | "-?" | "--help") :: _ => { | ||
| 125 | println(help) | ||
| 126 | sys.exit(0) | ||
| 127 | } | ||
| 128 | case flag @ ("-l" | "--logger") :: _level :: tail => { | ||
| 129 | // val level = _level match { | ||
| 130 | // case "quiet" => Logger.QUIET | ||
| 131 | // case "debug" => Logger.DEBUG | ||
| 132 | // case "verbose" => Logger.VERBOSE | ||
| 133 | // case _ => Logger.NORMAL | ||
| 134 | // } | ||
| 135 | parse(tail, config += ('logger -> _level)) | ||
| 136 | } | ||
| 137 | case flag @ ("-a" | "--answers") :: answers :: tail => | ||
| 138 | parse(tail, config += ('answers -> getPath(answers))) | ||
| 139 | case flag @ ("-x" | "--approximation") :: approx :: tail => { | ||
| 140 | parse(tail, config += ('approximation -> Symbol(approx))) | ||
| 141 | } | ||
| 142 | case flag @ ("-t" | "--transitive") :: tail => | ||
| 143 | parse(tail, config += ('transitive -> true)) | ||
| 144 | case flag @ ("-q" | "--queries") :: _query :: tail => { | ||
| 145 | val query = getPath(_query) | ||
| 146 | val files = | ||
| 147 | if (os.isFile(query)) | ||
| 148 | List(query) | ||
| 149 | else if (os.isDir(query)) | ||
| 150 | os.walk(query).filter(os.isFile).toList | ||
| 151 | else | ||
| 152 | exit(s"'${_query}' is not a valid path.") | ||
| 153 | parse(tail, config += ('queries -> files)) | ||
| 154 | } | ||
| 155 | case flag @ ("-o" | "--ontology") :: _ontology :: tail => { | ||
| 156 | val ontology = getPath(_ontology) | ||
| 157 | if (!os.isFile(ontology)) | ||
| 158 | exit(s"'${_ontology}' is not a valid filename.") | ||
| 159 | parse(tail, config += ('ontology -> ontology)) | ||
| 160 | } | ||
| 161 | case flag @ ("-d" | "--data") :: _data :: tail => { | ||
| 162 | val data = getPath(_data) | ||
| 163 | val files = | ||
| 164 | if (os.isFile(data)) | ||
| 165 | List(data) | ||
| 166 | else if (os.isDir(data)) { | ||
| 167 | os.walk(data).filter(os.isFile).toList | ||
| 168 | }else | ||
| 169 | exit(s"'${_data}' is not a valid path.") | ||
| 170 | parse(tail, config += ('data -> files)) | ||
| 171 | } | ||
| 172 | case a => exit(s"Invalid sequence of arguments '${a.mkString(" ")}'.") | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | /** Perform final checks on parsed options. | ||
| 177 | * | ||
| 178 | * @param config a parsed configuration | ||
| 179 | * @returns the input configuration, unchanged | ||
| 180 | */ | ||
| 181 | private def finalise(config: Config): Config = { | ||
| 182 | if (!config.contains('ontology)) | ||
| 183 | exit("The following flag is mandatory: '-o' or '--ontology'.") | ||
| 184 | config | ||
| 185 | } | ||
| 186 | |||
| 187 | /** Generate summary of a config object suitable for printing | ||
| 188 | * | ||
| 189 | * @param config a parsed configuration | ||
| 190 | * @returns a string describing the configuration | ||
| 191 | */ | ||
| 192 | def describe(config: Config): Unit = { | ||
| 193 | // config foreach { case (k,v) => k match { | ||
| 194 | // case 'logger => Logger print s"Logger level: ${v.get[Logger.Level]}" | ||
| 195 | // case 'ontology => Logger print s"Ontology file: ${v.get[os.Path]}" | ||
| 196 | // case 'data => { | ||
| 197 | // val paths = v.get[List[os.Path]] | ||
| 198 | // val ellipsis = if (paths.length > 1) " [...]" else "" | ||
| 199 | // Logger print s"Data files: ${paths.head}$ellipsis" | ||
| 200 | // } | ||
| 201 | // case 'queries => { | ||
| 202 | // val paths = v.get[List[os.Path]] | ||
| 203 | // val ellipsis = if (paths.length > 1) " [...]" else "" | ||
| 204 | // Logger print s"Query files: ${paths.head}$ellipsis" | ||
| 205 | // } | ||
| 206 | // case 'answers => Logger print s"Path to answers: ${v.get[os.Path]}" | ||
| 207 | // case 'approximation => Logger print s"Applied approximation: ${v.get[Symbol].name}" | ||
| 208 | // case 'transitive => Logger print s"Include property chain axioms: ${v.get[Boolean]}" | ||
| 209 | // }} | ||
| 210 | } | ||
| 211 | } | ||
