Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ private void subClass(X s, X p, X o, Output<X> out) {
* [rdfs6: (?a ?p ?b), (?p rdfs:subPropertyOf ?q) -> (?a ?q ?b)]
*/
private void subProperty(X s, X p, X o, Output<X> out) {
if ( SHORT_CIRCUIT && ! setup.hasClassDeclarations() )
if ( SHORT_CIRCUIT && ! setup.hasPropertyDeclarations() )
return;
Set<X> x = setup.getSuperProperties(p);
x.forEach(p2 -> derive(s, p2, o, out));
Expand All @@ -115,7 +115,7 @@ final private void domain(X s, X p, X o, Output<X> out) {
if ( ! setup.hasDomainDeclarations() )
return;
}
Set<X> x = setup.getDomain(p);
Set<X> x = getDomainInc(p);
x.forEach(c -> {
derive(s, rdfType, c, out);
subClass(s, rdfType, c, out);
Expand All @@ -131,7 +131,7 @@ final private void range(X s, X p, X o, Output<X> out) {
if ( ! setup.hasRangeDeclarations() )
return;
}
Set<X> x = setup.getRange(p);
Set<X> x = getRangeInc(p);
if (!mapper.isLiteral(o)) {
x.forEach(c -> {
derive(o, rdfType, c, out);
Expand Down
44 changes: 44 additions & 0 deletions jena-arq/src/main/java/org/apache/jena/rdfs/engine/CxtInf.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@

package org.apache.jena.rdfs.engine;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;

import org.apache.jena.graph.Node;
import org.apache.jena.rdfs.setup.ConfigRDFS;
Expand Down Expand Up @@ -64,4 +67,45 @@ protected boolean isANY(X x) {
protected boolean isTerm(X x) {
return !isANY(x);
}

// A triple using a property also uses each of its super-properties
// (rdfs:subPropertyOf), so the domains and ranges of the super-properties
// apply to it as well.

/** The domains of a property and of its super-properties. */
protected Set<X> getDomainInc(X property) {
return withSuperProperties(property, setup::getDomain);
}

/** The ranges of a property and of its super-properties. */
protected Set<X> getRangeInc(X property) {
return withSuperProperties(property, setup::getRange);
}

/** The properties with a given domain, and their sub-properties. */
protected Set<X> getPropertiesByDomainInc(X type) {
return withSubProperties(setup.getPropertiesByDomain(type));
}

/** The properties with a given range, and their sub-properties. */
protected Set<X> getPropertiesByRangeInc(X type) {
return withSubProperties(setup.getPropertiesByRange(type));
}

private Set<X> withSuperProperties(X property, Function<X, Set<X>> lookup) {
Set<X> superProperties = setup.getSuperProperties(property);
if ( superProperties.isEmpty() )
return lookup.apply(property);
Set<X> acc = new HashSet<>(lookup.apply(property));
superProperties.forEach(p -> acc.addAll(lookup.apply(p)));
return acc;
}

private Set<X> withSubProperties(Set<X> properties) {
if ( properties.isEmpty() || ! setup.hasPropertyDeclarations() )
return properties;
Set<X> acc = new HashSet<>(properties);
properties.forEach(p -> acc.addAll(setup.getSubProperties(p)));
return acc;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ private void accInstances(Set<T> tuples, Set<X> types, X requestedType) {

private void accInstancesDomain(Set<T> tuples, Set<X> types, X requestedType) {
for ( X type : types ) {
Set<X> predicates = setup.getPropertiesByDomain(type);
Set<X> predicates = getPropertiesByDomainInc(type);
if ( isEmpty(predicates) )
continue;
predicates.forEach(p -> {
Expand All @@ -388,7 +388,7 @@ private void accInstancesDomain(Set<T> tuples, Set<X> types, X requestedType) {

private void accInstancesRange(Set<T> tuples, Set<X> types, X requestedType) {
for ( X type : types ) {
Set<X> predicates = setup.getPropertiesByRange(type);
Set<X> predicates = getPropertiesByRangeInc(type);
if ( isEmpty(predicates) )
continue;
predicates.forEach(p -> {
Expand All @@ -407,7 +407,7 @@ private void accTypesDomain(Set<X> types, X X) {
Stream<T> stream = sourceFind(X, ANY, ANY);
stream.forEach(triple -> {
X p = predicate(triple);
Set<X> x = setup.getDomain(p);
Set<X> x = getDomainInc(p);
types.addAll(x);
});
}
Expand All @@ -416,7 +416,7 @@ private void accTypesRange(Set<X> types, X X) {
Stream<T> stream = sourceFind(ANY, ANY, X);
stream.forEach(triple -> {
X p = predicate(triple);
Set<X> x = setup.getRange(p);
Set<X> x = getRangeInc(p);
types.addAll(x);
});
}
Expand Down
25 changes: 25 additions & 0 deletions jena-arq/src/test/java/org/apache/jena/rdfs/TestMiscRDFS.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,31 @@ public class TestMiscRDFS {
assertEquals(5, count(rdfs, ANY, ANY, ANY));
}

// The domain of a super-property, with no rdfs:subClassOf in the vocabulary.
@Test public void subPropertyDomain() {
Graph vocab = SSE.parseGraph("(graph (:p rdfs:subPropertyOf :q) (:q rdfs:domain :D) )");
Graph data = SSE.parseGraph("(graph (:x :p :y))");
Graph rdfs = RDFSFactory.graphRDFS(data, vocab);
exactlyOne(rdfs, node(":x"), node(":q"), node(":y"));
exactlyOne(rdfs, node(":x"), rdfType, node(":D"));
exactlyOne(rdfs, node(":x"), rdfType, ANY);
exactlyOne(rdfs, ANY, rdfType, node(":D"));
exactlyOne(rdfs, ANY, rdfType, ANY);
assertEquals(3, count(rdfs, ANY, ANY, ANY));
assertEquals(3, count(rdfs, node(":x"), ANY, ANY));
}

// The range of a super-property, with no rdfs:subClassOf in the vocabulary.
@Test public void subPropertyRange() {
Graph vocab = SSE.parseGraph("(graph (:p rdfs:subPropertyOf :q) (:q rdfs:range :R) )");
Graph data = SSE.parseGraph("(graph (:x :p :y))");
Graph rdfs = RDFSFactory.graphRDFS(data, vocab);
exactlyOne(rdfs, node(":y"), rdfType, node(":R"));
exactlyOne(rdfs, node(":y"), rdfType, ANY);
exactlyOne(rdfs, ANY, rdfType, node(":R"));
assertEquals(3, count(rdfs, ANY, ANY, ANY));
}

@Test public void dataset() {
Graph vocab = SSE.parseGraph("(graph (:S rdfs:subClassOf :T))");
DatasetGraph dsg0 = DatasetGraphFactory.createTxnMem();
Expand Down
86 changes: 86 additions & 0 deletions jena-arq/src/test/java/org/apache/jena/rdfs/TestStreamRDFS.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,83 @@ public class TestStreamRDFS {
assertTrue(listContains(results, "(:X rdf:type :U)"));
}

// A sub-property of a property with a domain, in the shared test vocabulary.
@Test public void infer_3() {
Triple t = SSE.parseTriple("(:c :pp :x)");
List<Triple> results = infOutput(x->x.triple(t));

Set<Triple> resultSet = new HashSet<>(results);
assertEquals(5, resultSet.size());
assertTrue(listContains(results, "(:c :pp :x)"));
assertTrue(listContains(results, "(:c :p :x)"));
assertTrue(listContains(results, "(:c :pTop :x)"));
assertTrue(listContains(results, "(:c rdf:type :Q)"));
assertTrue(listContains(results, "(:c rdf:type :Q2)"));
}

// rdfs:subPropertyOf in a vocabulary with no rdfs:subClassOf.
@Test public void subProperty_1() {
Graph vocab = SSE.parseGraph("(graph (:p rdfs:subPropertyOf :q))");
List<Triple> results = infOutput(vocab, "(:x :p :y)");

assertEquals(2, results.size());
assertTrue(listContains(results, "(:x :p :y)"));
assertTrue(listContains(results, "(:x :q :y)"));
}

@Test public void subProperty_2() {
Graph vocab = SSE.parseGraph("(graph (:p rdfs:subPropertyOf :q) (:q rdfs:subPropertyOf :r))");
List<Triple> results = infOutput(vocab, "(:x :p :y)");

assertEquals(3, results.size());
assertTrue(listContains(results, "(:x :q :y)"));
assertTrue(listContains(results, "(:x :r :y)"));
}

// The domain of a super-property.
@Test public void subPropertyDomain_1() {
Graph vocab = SSE.parseGraph("(graph (:p rdfs:subPropertyOf :q) (:q rdfs:domain :D))");
List<Triple> results = infOutput(vocab, "(:x :p :y)");

assertEquals(3, results.size());
assertTrue(listContains(results, "(:x :q :y)"));
assertTrue(listContains(results, "(:x rdf:type :D)"));
}

// The range of a super-property.
@Test public void subPropertyRange_1() {
Graph vocab = SSE.parseGraph("(graph (:p rdfs:subPropertyOf :q) (:q rdfs:range :R))");
List<Triple> results = infOutput(vocab, "(:x :p :y)");

assertEquals(3, results.size());
assertTrue(listContains(results, "(:x :q :y)"));
assertTrue(listContains(results, "(:y rdf:type :R)"));
}

// The domain of a super-property of a super-property, then its super-class.
@Test public void subPropertyDomain_2() {
Graph vocab = SSE.parseGraph("""
(graph (:p rdfs:subPropertyOf :q) (:q rdfs:subPropertyOf :r)
(:r rdfs:domain :D) (:D rdfs:subClassOf :E))
""");
List<Triple> results = infOutput(vocab, "(:x :p :y)");

assertEquals(5, results.size());
assertTrue(listContains(results, "(:x :q :y)"));
assertTrue(listContains(results, "(:x :r :y)"));
assertTrue(listContains(results, "(:x rdf:type :D)"));
assertTrue(listContains(results, "(:x rdf:type :E)"));
}

// The same domain on a property and its super-property is inferred once.
@Test public void subPropertyDomain_3() {
Graph vocab = SSE.parseGraph("(graph (:p rdfs:subPropertyOf :q) (:p rdfs:domain :D) (:q rdfs:domain :D))");
List<Triple> results = infOutput(vocab, "(:x :p :y)");

assertEquals(3, results.size());
assertTrue(listContains(results, "(:x rdf:type :D)"));
}

private static boolean listContains(List<Triple> list, String strTriple) {
Triple triple = SSE.parseTriple(strTriple);
return list.stream().anyMatch(t->triple.equals(t));
Expand All @@ -115,6 +192,15 @@ private static List<Triple> infOutput(Consumer<StreamRDF> action) {
return dest.getTriples();
}

// Inference of one triple, with a given vocabulary, to a list
private static List<Triple> infOutput(Graph vocabulary, String strTriple) {
Triple t = SSE.parseTriple(strTriple);
CollectorStreamRDF dest = new CollectorStreamRDF();
StreamRDF stream = RDFSFactory.streamRDFS(dest, vocabulary);
exec(stream, x->x.triple(t));
return dest.getTriples();
}

private static void exec(StreamRDF stream, Consumer<StreamRDF> action) {
stream.start();
try {
Expand Down