1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
package uk.ac.ox.cs.rsacomb
import java.io.File
import java.util.HashMap
import scala.collection.JavaConverters._
import tech.oxfordsemantic.jrdfox.client.UpdateType
import tech.oxfordsemantic.jrdfox.logic.expression.{IRI, Term}
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] <ontology> [<data> ...]
-h | -? | --help
print this help message
--rsacheck-only
only perform the RSA check without performing any query answering.
-q <file> | --query <file>
path to a file containing a single SPARQL query
<ontology>
file containing the ontology
<data>
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
* ontology files as arguments. The query file is expected to contain
* exactly one query, while the ontology files will be programmatically
* merged in a single ontology.
*
* @todo better arguments handling is needed. Look into some library
* for this.
* @todo at the moment the input ontology is assumed to be Horn-ALCHOIQ.
* This might not be the case.
*/
object RSAComb extends App {
val config = RSAConfig.parse(args.toList)
val ontology =
RSAOntology(config('ontology).get[File], config('data).get[List[File]]: _*)
if (ontology.isRSA) {
Logger print "Ontology is RSA!"
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")
}
}
} else {
Logger print "Ontology is not RSA!"
}
}
|