aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala480
-rw-r--r--src/test/scala/uk/ac/ox/cs/rsacomb/RDFoxConverterSpec.scala104
2 files changed, 584 insertions, 0 deletions
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala
new file mode 100644
index 0000000..9b2071e
--- /dev/null
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala
@@ -0,0 +1,480 @@
1package uk.ac.ox.cs.rsacomb.converter
2
3import java.util.stream.Collectors
4import org.semanticweb.owlapi.model.{
5 OWLAnnotationProperty,
6 OWLLogicalAxiom,
7 OWLClass,
8 OWLClassAssertionAxiom,
9 OWLClassExpression,
10 OWLDataProperty,
11 OWLDataPropertyDomainAxiom,
12 OWLDataPropertyExpression,
13 OWLDataSomeValuesFrom,
14 OWLEquivalentClassesAxiom,
15 OWLEquivalentObjectPropertiesAxiom,
16 OWLInverseObjectPropertiesAxiom,
17 OWLNamedIndividual,
18 OWLObjectIntersectionOf,
19 OWLObjectInverseOf,
20 OWLObjectMaxCardinality,
21 OWLObjectOneOf,
22 OWLObjectProperty,
23 OWLObjectPropertyAssertionAxiom,
24 OWLObjectPropertyDomainAxiom,
25 OWLObjectPropertyExpression,
26 OWLObjectPropertyRangeAxiom,
27 OWLObjectSomeValuesFrom,
28 OWLPropertyExpression,
29 OWLSubClassOfAxiom,
30 OWLSubObjectPropertyOfAxiom
31}
32import scala.collection.JavaConverters._
33import tech.oxfordsemantic.jrdfox.logic.datalog.{
34 BindAtom,
35 BodyFormula,
36 Rule,
37 TupleTableAtom
38}
39import tech.oxfordsemantic.jrdfox.logic.expression.{Term, IRI, FunctionCall}
40import uk.ac.ox.cs.rsacomb.RSAOntology
41import uk.ac.ox.cs.rsacomb.suffix.{RSASuffix, Inverse}
42import uk.ac.ox.cs.rsacomb.util.RSA
43
44/** Horn-ALCHOIQ to RDFox axiom converter.
45 *
46 * Provides the tools to translate Horn-ALCHOIQ axioms into logic rules
47 * using RDFox syntax.
48 *
49 * @note the input axioms are assumed to be normalized. Trying to
50 * convert non normalized axioms might result in undefined behavious.
51 * We use the normalization defined in the main paper.
52 *
53 * @see [[https://github.com/KRR-Oxford/RSA-combined-approach GitHub repository]]
54 * for more information on the theoretical aspects of the system.
55 *
56 * @todo this is not ideal and it would be more sensible to prepend a
57 * normalization procedure that will prevent errors or unexpected
58 * results.
59 */
60object RDFoxConverter {
61
62 /** Simplify conversion between Java and Scala collections */
63 import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._
64
65 /** Simplify conversion between similar concepts in OWLAPI and RDFox
66 * abstract syntax.
67 */
68 import uk.ac.ox.cs.rsacomb.implicits.RDFox._
69
70 /** Represents the result of the conversion of a
71 * [[org.semanticweb.owlapi.model.OWLClassExpression OWLClassExpression]].
72 *
73 * In general a class expression is translated into a list of
74 * [[tech.oxfordsemantic.jrdfox.logic.datalog.TupleTableAtom TupleTableAtoms]].
75 * In some cases a class appearing on the right of a GCI might
76 * generate additional atoms that will appear in the body of the
77 * resulting formula.
78 *
79 * @example
80 * In `A ⊑ ≤1R.B`, translated as
81 * ```
82 * y = z <- A(x), R(x,y), B(y), R(x,z), B(z)
83 * ```
84 * the atom `≤1R.B` produces `y = z` to appear as head of the rule,
85 * along with a set of atoms for the body of the rule (namely
86 * `R(x,y), B(y), R(x,z), B(z)`).
87 */
88 private type Shards = (List[TupleTableAtom], List[BodyFormula])
89
90 /** Represent the result of the conversion of
91 * [[org.semanticweb.owlapi.model.OWLLogicalAxiom OWLLogicalAxiom]].
92 *
93 * In general we have assertion returning (a collection of) atoms,
94 * while other axioms that generate rules.
95 */
96 private type Result = Either[List[TupleTableAtom], List[Rule]]
97
98 /** Converts a
99 * [[org.semanticweb.owlapi.model.OWLLogicalAxiom OWLLogicalAxiom]]
100 * into a collection of
101 * [[tech.oxfordsemantic.jrdfox.logic.datalog.TupleTableAtom TupleTableAtoms]]
102 * and
103 * [[tech.oxfordsemantic.jrdfox.logic.datalog.Rule Rules]].
104 *
105 * @note not all possible axioms are handled correctly, and in
106 * general they are assumed to be normalised. Following is a list of
107 * all unhandled class expressions:
108 * - [[org.semanticweb.owlapi.model.OWLAsymmetricObjectPropertyAxiom OWLAsymmetricObjectPropertyAxiom]]
109 * - [[org.semanticweb.owlapi.model.OWLDataPropertyAssertionAxiom OWLDataPropertyAssertionAxiom]]
110 * - [[org.semanticweb.owlapi.model.OWLDataPropertyRangeAxiom OWLDataPropertyRangeAxiom]]
111 * - [[org.semanticweb.owlapi.model.OWLDatatypeDefinitionAxiom OWLDatatypeDefinitionAxiom]]
112 * - [[org.semanticweb.owlapi.model.OWLDifferentIndividualsAxiom OWLDifferentIndividualsAxiom]]
113 * - [[org.semanticweb.owlapi.model.OWLDisjointClassesAxiom OWLDisjointClassesAxiom]]
114 * - [[org.semanticweb.owlapi.model.OWLDisjointDataPropertiesAxiom OWLDisjointDataPropertiesAxiom]]
115 * - [[org.semanticweb.owlapi.model.OWLDisjointObjectPropertiesAxiom OWLDisjointObjectPropertiesAxiom]]
116 * - [[org.semanticweb.owlapi.model.OWLDisjointUnionAxiom OWLDisjointUnionAxiom]]
117 * - [[org.semanticweb.owlapi.model.OWLEquivalentDataPropertiesAxiom OWLEquivalentDataPropertiesAxiom]]
118 * - [[org.semanticweb.owlapi.model.OWLFunctionalDataPropertyAxiom OWLFunctionalDataPropertyAxiom]]
119 * - [[org.semanticweb.owlapi.model.OWLFunctionalObjectPropertyAxiom OWLFunctionalObjectPropertyAxiom]]
120 * - [[org.semanticweb.owlapi.model.OWLHasKeyAxiom OWLHasKeyAxiom]]
121 * - [[org.semanticweb.owlapi.model.OWLInverseFunctionalObjectPropertyAxiom OWLInverseFunctionalObjectPropertyAxiom]]
122 * - [[org.semanticweb.owlapi.model.OWLIrreflexiveObjectPropertyAxiom OWLIrreflexiveObjectPropertyAxiom]]
123 * - [[org.semanticweb.owlapi.model.OWLNegativeDataPropertyAssertionAxiom OWLNegativeDataPropertyAssertionAxiom]]
124 * - [[org.semanticweb.owlapi.model.OWLNegativeObjectPropertyAssertionAxiom OWLNegativeObjectPropertyAssertionAxiom]]
125 * - [[org.semanticweb.owlapi.model.OWLReflexiveObjectPropertyAxiom OWLReflexiveObjectPropertyAxiom]]
126 * - [[org.semanticweb.owlapi.model.OWLSameIndividualAxiom OWLSameIndividualAxiom]]
127 * - [[org.semanticweb.owlapi.model.OWLSubDataPropertyOfAxiom OWLSubDataPropertyOfAxiom]]
128 * - [[org.semanticweb.owlapi.model.OWLSubPropertyChainOfAxiom OWLSubPropertyChainOfAxiom]]
129 * - [[org.semanticweb.owlapi.model.OWLSymmetricObjectPropertyAxiom OWLSymmetricObjectPropertyAxiom]]
130 * - [[org.semanticweb.owlapi.model.OWLTransitiveObjectPropertyAxiom OWLTransitiveObjectPropertyAxiom]]
131 * - [[org.semanticweb.owlapi.model.SWRLRule SWRLRule]]
132 */
133 def convert(
134 axiom: OWLLogicalAxiom,
135 term: Term,
136 unsafe: List[OWLObjectPropertyExpression],
137 skolem: SkolemStrategy,
138 suffix: RSASuffix
139 ): Result =
140 axiom match {
141
142 case a: OWLSubClassOfAxiom => {
143 val (sub, _) =
144 convert(a.getSubClass, term, unsafe, SkolemStrategy.None, suffix)
145 val (sup, ext) =
146 convert(a.getSuperClass, term, unsafe, skolem, suffix)
147 val rule = Rule.create(sup, ext ::: sub)
148 Right(List(rule))
149 }
150
151 // cannot be left
152 // http://www.w3.org/TR/owl2-syntax/#Equivalent_Classes
153 case a: OWLEquivalentClassesAxiom =>
154 Right(
155 a.asPairwiseAxioms
156 .flatMap(_.asOWLSubClassOfAxioms)
157 .map(convert(_, term, unsafe, skolem, suffix))
158 .collect { case Right(rs) => rs }
159 .flatten
160 )
161
162 case a: OWLEquivalentObjectPropertiesAxiom => {
163 Right(
164 a.asPairwiseAxioms
165 .flatMap(_.asSubObjectPropertyOfAxioms)
166 .map(convert(_, term, unsafe, skolem, suffix))
167 .collect { case Right(rs) => rs }
168 .flatten
169 )
170 }
171
172 case a: OWLSubObjectPropertyOfAxiom => {
173 val term1 = RSAOntology.genFreshVariable()
174 val body = convert(a.getSubProperty, term, term1, suffix)
175 val head = convert(a.getSuperProperty, term, term1, suffix)
176 Right(List(Rule.create(head, body)))
177 }
178
179 case a: OWLObjectPropertyDomainAxiom =>
180 convert(a.asOWLSubClassOfAxiom, term, unsafe, skolem, suffix)
181
182 case a: OWLObjectPropertyRangeAxiom => {
183 val term1 = RSAOntology.genFreshVariable()
184 val (res, ext) = convert(a.getRange, term, unsafe, skolem, suffix)
185 val prop = convert(a.getProperty, term1, term, suffix)
186 Right(List(Rule.create(res, prop :: ext)))
187 }
188
189 case a: OWLDataPropertyDomainAxiom =>
190 convert(a.asOWLSubClassOfAxiom, term, unsafe, skolem, suffix)
191
192 case a: OWLInverseObjectPropertiesAxiom =>
193 Right(
194 a.asSubObjectPropertyOfAxioms
195 .map(convert(_, term, unsafe, skolem, suffix))
196 .collect { case Right(rs) => rs }
197 .flatten
198 )
199
200 case a: OWLClassAssertionAxiom => {
201 val ind = a.getIndividual
202 ind match {
203 case i: OWLNamedIndividual => {
204 val cls = a.getClassExpression
205 val (res, _) =
206 convert(cls, i.getIRI, unsafe, SkolemStrategy.None, suffix)
207 Left(res)
208 }
209 case _ => Left(List())
210 }
211 }
212
213 case a: OWLObjectPropertyAssertionAxiom =>
214 if (!a.getSubject.isNamed || !a.getObject.isNamed)
215 Left(List())
216 else {
217 val subj = a.getSubject.asOWLNamedIndividual.getIRI
218 val obj = a.getObject.asOWLNamedIndividual.getIRI
219 val prop = convert(a.getProperty, subj, obj, suffix)
220 Left(List(prop))
221 }
222
223 /** Catch-all case for all unhandled axiom types. */
224 case a =>
225 throw new RuntimeException(
226 s"Axiom '$a' is not supported (yet?)"
227 )
228
229 }
230
231 /** Converts a class expression into a collection of atoms.
232 *
233 * @note not all possible class expressions are handled correctly.
234 * Following is a list of all unhandled class expressions:
235 * - [[org.semanticweb.owlapi.model.OWLDataAllValuesFrom OWLDataAllValuesFrom]]
236 * - [[org.semanticweb.owlapi.model.OWLDataExactCardinality OWLDataExactCardinality]]
237 * - [[org.semanticweb.owlapi.model.OWLDataMaxCardinality OWLDataMaxCardinality]]
238 * - [[org.semanticweb.owlapi.model.OWLDataMinCardinality OWLDataMinCardinality]]
239 * - [[org.semanticweb.owlapi.model.OWLDataHasValue OWLDataHasValue]]
240 * - [[org.semanticweb.owlapi.model.OWLObjectAllValuesFrom OWLObjectAllValuesFrom]]
241 * - [[org.semanticweb.owlapi.model.OWLObjectComplementOf OWLObjectComplementOf]]
242 * - [[org.semanticweb.owlapi.model.OWLObjectExactCardinality OWLObjectExactCardinality]]
243 * - [[org.semanticweb.owlapi.model.OWLObjectHasSelf OWLObjectHasSelf]]
244 * - [[org.semanticweb.owlapi.model.OWLObjectHasValue OWLObjectHasValue]]
245 * - [[org.semanticweb.owlapi.model.OWLObjectMinCardinality OWLObjectMinCardinality]]
246 * - [[org.semanticweb.owlapi.model.OWLObjectUnionOf OWLObjectUnionOf]]
247 *
248 * Moreover:
249 * - [[org.semanticweb.owlapi.model.OWLObjectMaxCardinality OWLObjectMaxCardinality]]
250 * is accepted only when cardinality is set to 1;
251 * - [[org.semanticweb.owlapi.model.OWLObjectOneOf OWLObjectOneOf]]
252 * is accepted only when its arity is 1.
253 */
254 def convert(
255 expr: OWLClassExpression,
256 term: Term,
257 unsafe: List[OWLObjectPropertyExpression],
258 skolem: SkolemStrategy,
259 suffix: RSASuffix
260 ): Shards =
261 expr match {
262
263 /** Simple class name.
264 *
265 * @see [[http://www.w3.org/TR/owl2-syntax/#Classes]]
266 */
267 case e: OWLClass => {
268 val iri: IRI = if (e.isTopEntity()) IRI.THING else e.getIRI
269 val atom = TupleTableAtom.rdf(term, IRI.RDF_TYPE, iri)
270 (List(atom), List())
271 }
272
273 /** Conjunction of class expressions.
274 *
275 * @see [[http://www.w3.org/TR/owl2-syntax/#Intersection_of_Class_Expressions]]
276 */
277 case e: OWLObjectIntersectionOf => {
278 val (res, ext) = e.asConjunctSet
279 .map(convert(_, term, unsafe, skolem, suffix))
280 .unzip
281 (res.flatten, ext.flatten)
282 }
283
284 /** Enumeration of individuals.
285 *
286 * @note we only admit enumerations of arity 1.
287 *
288 * @throws `RuntimeException` when dealing with an enumeration
289 * with arity != 1.
290 *
291 * @see [[http://www.w3.org/TR/owl2-syntax/#Enumeration_of_Individuals]]
292 */
293 case e: OWLObjectOneOf => {
294 val named = e.individuals
295 .collect(Collectors.toList())
296 .collect { case x: OWLNamedIndividual => x }
297 if (named.length != 1)
298 throw new RuntimeException(s"Class expression '$e' has arity != 1.")
299 val atom = TupleTableAtom.rdf(term, IRI.SAME_AS, named.head.getIRI)
300 (List(atom), List())
301 }
302
303 /** Existential class expression (for data properties).
304 *
305 * Parameter `skolem` is used to determine the skolemization
306 * technique (if any) to use for the translation.
307 *
308 * @see [[http://www.w3.org/TR/owl2-syntax/#Existential_Quantification]]
309 */
310 case e: OWLObjectSomeValuesFrom => {
311 val cls = e.getFiller()
312 val role = e.getProperty()
313 // TODO: simplify this:
314 // Computes the result of rule skolemization. Depending on the used
315 // technique it might involve the introduction of additional atoms,
316 // and/or fresh constants and variables.
317 val (head, body, term1) = skolem match {
318 case SkolemStrategy.None =>
319 (List(), List(), RSAOntology.genFreshVariable)
320 case SkolemStrategy.Constant(c) => (List(), List(), c)
321 case SkolemStrategy.ConstantRSA(c) => {
322 if (unsafe.contains(role))
323 (List(RSA.PE(term, c), RSA.U(c)), List(), c)
324 else
325 (List(), List(), c)
326 }
327 case SkolemStrategy.Standard(f) => {
328 val x = RSAOntology.genFreshVariable
329 (
330 List(),
331 List(BindAtom.create(FunctionCall.create("SKOLEM", f, term), x)),
332 x
333 )
334 }
335 }
336 val (res, ext) = convert(cls, term1, unsafe, skolem, suffix)
337 val prop = convert(role, term, term1, suffix)
338 (prop :: head ::: res, body ::: ext)
339 }
340
341 /** Existential class expression (for data properties).
342 *
343 * Parameter `skolem` is used to determine the skolemization
344 * technique (if any) to use for the translation.
345 *
346 * @todo the "filler" of this OWL expression is currently ignored.
347 * This, in general might not be how we want to handle
348 * [[org.semanticweb.owlapi.model.OWLDataRange OWLDataRanges]].
349 *
350 * @see [[http://www.w3.org/TR/owl2-syntax/#Existential_Quantification_2]]
351 */
352 case e: OWLDataSomeValuesFrom => {
353 val role = e.getProperty()
354 // TODO: simplify this:
355 // Computes the result of rule skolemization. Depending on the used
356 // technique it might involve the introduction of additional atoms,
357 // and/or fresh constants and variables.
358 val (head, body, term1) = skolem match {
359 case SkolemStrategy.None =>
360 (List(), List(), RSAOntology.genFreshVariable)
361 case SkolemStrategy.Constant(c) => (List(), List(), c)
362 case SkolemStrategy.ConstantRSA(c) => {
363 if (unsafe.contains(role))
364 (List(RSA.PE(term, c), RSA.U(c)), List(), c)
365 else
366 (List(), List(), c)
367 }
368 case SkolemStrategy.Standard(f) => {
369 val y = RSAOntology.genFreshVariable()
370 (
371 List(),
372 List(BindAtom.create(FunctionCall.create("SKOLEM", f, term), y)),
373 y
374 )
375 }
376 }
377 val prop = convert(role, term, term1, suffix)
378 (prop :: head, body)
379 }
380
381 /** Maximum cardinality restriction class
382 *
383 * @note we only admit classes with cardinality set to 1.
384 *
385 * @throws `RuntimeException` when dealing with a restriction
386 * with cardinality != 1.
387 *
388 * @see [[http://www.w3.org/TR/owl2-syntax/#Maximum_Cardinality_2]]
389 */
390 case e: OWLObjectMaxCardinality => {
391 if (e.getCardinality != 1)
392 throw new RuntimeException(
393 s"Class expression '$e' has cardinality restriction != 1."
394 )
395 val vars @ (y :: z :: _) =
396 Seq(RSAOntology.genFreshVariable(), RSAOntology.genFreshVariable())
397 val cls = e.getFiller
398 val role = e.getProperty
399 val (res, ext) = vars.map(convert(cls, _, unsafe, skolem, suffix)).unzip
400 val props = vars.map(convert(role, term, _, suffix))
401 val eq = TupleTableAtom.rdf(y, IRI.SAME_AS, z)
402 (List(eq), res.flatten ++ props)
403 }
404
405 /** Catch-all case for all unhandled class expressions. */
406 case e =>
407 throw new RuntimeException(
408 s"Class expression '$e' is not supported (yet?)"
409 )
410 }
411
412 /** Converts an object property expression into an atom. */
413 def convert(
414 expr: OWLObjectPropertyExpression,
415 term1: Term,
416 term2: Term,
417 suffix: RSASuffix
418 ): TupleTableAtom =
419 expr match {
420
421 /** Simple named role/object property.
422 *
423 * @see [[http://www.w3.org/TR/owl2-syntax/#Object_Properties Object Properties]]
424 */
425 case e: OWLObjectProperty => {
426 val role = IRI.create(e.getIRI.getIRIString :: suffix)
427 TupleTableAtom.rdf(term1, role, term2)
428 }
429
430 /** Inverse of a named role/property
431 *
432 * OWLAPI does not admit nesting of negation, and double
433 * negations are always simplified.
434 *
435 * @see [[https://www.w3.org/TR/owl2-syntax/#Inverse_Object_Properties Inverse Object Properties]]
436 */
437 case e: OWLObjectInverseOf =>
438 convert(e.getInverse, term1, term2, suffix + Inverse)
439
440 /** The infamous impossible case.
441 *
442 * @note all relevant cases are taken care of, and this branch
443 * throws a runtime exception to notify of the problem.
444 */
445 case e =>
446 throw new RuntimeException(
447 s"Unable to convert '$e' into a logic expression. This should be happening (TM)."
448 )
449 }
450
451 /** Converts a data property expression into an atom. */
452 def convert(
453 expr: OWLDataPropertyExpression,
454 term1: Term,
455 term2: Term,
456 suffix: RSASuffix
457 ): TupleTableAtom =
458 expr match {
459
460 /** Simple named role/data property
461 *
462 * @see [[https://www.w3.org/TR/owl2-syntax/#Datatypes Data Properties]]
463 */
464 case e: OWLDataProperty => {
465 val role = IRI.create(e.getIRI.getIRIString :: suffix)
466 TupleTableAtom.rdf(term1, role, term2)
467 }
468
469 /** The infamous impossible case.
470 *
471 * @note all relevant cases are taken care of, and this branch
472 * throws a runtime exception to notify of the problem.
473 */
474 case e =>
475 throw new RuntimeException(
476 s"Unable to convert '$e' into a logic expression. This should be happening (TM)."
477 )
478 }
479
480}
diff --git a/src/test/scala/uk/ac/ox/cs/rsacomb/RDFoxConverterSpec.scala b/src/test/scala/uk/ac/ox/cs/rsacomb/RDFoxConverterSpec.scala
new file mode 100644
index 0000000..35af464
--- /dev/null
+++ b/src/test/scala/uk/ac/ox/cs/rsacomb/RDFoxConverterSpec.scala
@@ -0,0 +1,104 @@
1package rsacomb
2
3import org.scalatest.LoneElement
4import org.scalatest.flatspec.AnyFlatSpec
5import org.scalatest.matchers.should.Matchers
6import org.semanticweb.owlapi.apibinding.OWLManager
7import org.semanticweb.owlapi.model.OWLOntologyManager
8
9import tech.oxfordsemantic.jrdfox.logic.datalog.TupleTableAtom
10import tech.oxfordsemantic.jrdfox.logic.expression.{Variable, IRI}
11import uk.ac.ox.cs.rsacomb.converter.RDFoxConverter
12import uk.ac.ox.cs.rsacomb.suffix.{Empty, Forward, Backward, Inverse}
13import uk.ac.ox.cs.rsacomb.converter.SkolemStrategy
14
15object RDFoxConverterSpec {
16
17 val manager = OWLManager.createOWLOntologyManager()
18 val factory = manager.getOWLDataFactory
19
20 val term0 = Variable.create("X")
21 val term1 = Variable.create("Y")
22 val iriString0 = "http://example.com/rsacomb/iri0"
23 val iriString1 = "http://example.com/rsacomb/iri1"
24 val iriString2 = "http://example.com/rsacomb/iri2"
25 val suffixes = Seq(
26 Empty,
27 Forward,
28 Backward,
29 Inverse,
30 Forward + Inverse,
31 Backward + Inverse
32 )
33}
34
35class RDFoxConverterSpec extends AnyFlatSpec with Matchers with LoneElement {
36
37 import RDFoxConverterSpec._
38
39 "A class name" should "be converted into a single atom" in {
40 val cls = factory.getOWLClass(iriString0)
41 val atom = TupleTableAtom.rdf(term0, IRI.RDF_TYPE, IRI.create(iriString0))
42 val (res, ext) =
43 RDFoxConverter.convert(cls, term0, List(), SkolemStrategy.None, Empty)
44 res.loneElement shouldEqual atom
45 ext shouldBe empty
46 }
47
48 "A intersection of classes" should "be converted into the union of the conversion of the classes" in {
49 val cls0 = factory.getOWLClass(iriString0)
50 val cls1 = factory.getOWLClass(iriString1)
51 val cls2 = factory.getOWLClass(iriString2)
52 val conj = factory.getOWLObjectIntersectionOf(cls0, cls1, cls2)
53 val (res0, ext0) =
54 RDFoxConverter.convert(cls0, term0, List(), SkolemStrategy.None, Empty)
55 val (res1, ext1) =
56 RDFoxConverter.convert(cls1, term0, List(), SkolemStrategy.None, Empty)
57 val (res2, ext2) =
58 RDFoxConverter.convert(cls2, term0, List(), SkolemStrategy.None, Empty)
59 val (res, ext) =
60 RDFoxConverter.convert(conj, term0, List(), SkolemStrategy.None, Empty)
61 res should contain theSameElementsAs (res0 ::: res1 ::: res2)
62 ext should contain theSameElementsAs (ext0 ::: ext1 ::: ext2)
63 }
64
65 "A singleton intersection" should "correspond to the conversion of the internal class" in {
66 val cls0 = factory.getOWLClass(iriString0)
67 val conj = factory.getOWLObjectIntersectionOf(cls0)
68 val (res0, ext0) =
69 RDFoxConverter.convert(cls0, term0, List(), SkolemStrategy.None, Empty)
70 val (res, ext) =
71 RDFoxConverter.convert(conj, term0, List(), SkolemStrategy.None, Empty)
72 res should contain theSameElementsAs res0
73 ext should contain theSameElementsAs ext0
74 }
75
76 "An object property" should "be converted into an atom with matching predicate" in {
77 val prop = factory.getOWLObjectProperty(iriString0)
78 for (sx <- suffixes) {
79 val atom =
80 TupleTableAtom.rdf(term0, IRI.create(iriString0 :: sx), term1)
81 RDFoxConverter.convert(prop, term0, term1, sx) shouldEqual atom
82 }
83 }
84
85 "The inverse of an object property" should "be converted into an atom with matching negated predicate" in {
86 val prop = factory.getOWLObjectProperty(iriString0)
87 val inv = factory.getOWLObjectInverseOf(prop)
88 for (sx <- Seq(Empty, Forward, Backward)) {
89 val atom =
90 TupleTableAtom.rdf(term0, IRI.create(iriString0 :: sx + Inverse), term1)
91 RDFoxConverter.convert(inv, term0, term1, sx) shouldEqual atom
92 }
93 }
94
95 "A data property" should "be converted into an atom with matching predicate" in {
96 val prop = factory.getOWLDataProperty(iriString0)
97 for (suffix <- suffixes) {
98 val atom =
99 TupleTableAtom.rdf(term0, IRI.create(iriString0 :: suffix), term1)
100 RDFoxConverter.convert(prop, term0, term1, suffix) shouldEqual atom
101 }
102 }
103
104}