blob: e7455322ac2b51962dca1979294a6def323b8015 [file] [log] [blame]
package org.apache.rya.reasoning;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Resource;
/**
* Contains all the schema information we might need about a property.
*
* Rules implemented dynamically by getter methods:
*
* scm-op States that every object property is its own subproperty and
* equivalent property.
* scm-dp States that every datatype property is its own subproperty and
* equivalent property.
* scm-eqp1 States that if two properties are equivalent, they are also
* subproperties. Equivalence is represented this way internally.
* scm-eqp2 States that if two properties are each other's subproperties,
* they are also equivalent properties.
*
* Rules implemented explicitly by calling methods once all the necessary data
* has been read in:
*
* scm-spo States that subPropertyOf is transitive; computed by
* computeSuperProperties().
* scm-dom1 States that a property with domain c also has as domain any
* of c's superclasses. Computed in inheritDomainRange().
* scm-dom2 States that a property inherits its superproperties' domains.
* Computed in inheritDomainRange().
* scm-rng1 States that a property with range c also has as domain any
* of c's superclasses. Computed in inheritDomainRange().
* scm-rng2 States that a property inherits its superproperties' ranges.
* Computed in inheritDomainRange().
* svm-svf1 States that property restriction c1 is a subclass of another c2 if
* they are someValuesFrom restrictions on the same property where
* c1's target class is a subclass of c2's target class.
* Computed in compareRestrictions(), depends on subclass info.
* svm-avf1 States that property restriction c1 is a subclass of another c2 if
* they are allValuesFrom restrictions on the same property where
* c1's target class is a subclass of c2's target class.
* Computed in compareRestrictions(), depends on subclass info.
*/
public class OwlProperty implements Serializable {
private static final long serialVersionUID = 1L;
private IRI uri;
// Boolean qualities the property might have
private boolean transitive = false;
private boolean symmetric = false;
private boolean asymmetric = false;
private boolean functional = false;
private boolean inverseFunctional = false;
private boolean irreflexive = false;
// Relations to other properties
private Set<OwlProperty> superProperties = new HashSet<OwlProperty>();
private Set<OwlProperty> disjointProperties = new HashSet<OwlProperty>();
private Set<OwlProperty> inverseProperties = new HashSet<OwlProperty>();
// Relations to classes
private Set<OwlClass> domain = new HashSet<OwlClass>();
private Set<OwlClass> range = new HashSet<OwlClass>();
// Restrictions on this property
private Set<OwlClass> restrictions = new HashSet<OwlClass>();
OwlProperty(IRI uri) {
this.uri = uri;
}
boolean addSuperProperty(OwlProperty p) {
return superProperties.add(p);
}
/**
* Add an equivalient property
* RL rule scm-eqp1: Store equivalence as mutual subPropertyOf relations
*/
boolean addEquivalentProperty(OwlProperty p) {
boolean change = this.superProperties.add(p);
change = p.superProperties.add(this) || change;
return change;
}
boolean addDisjoint(OwlProperty p) { return disjointProperties.add(p); }
boolean addInverse(OwlProperty p) { return inverseProperties.add(p); }
boolean addDomain(OwlClass c) {
return domain.add(c);
}
boolean addRange(OwlClass c) {
return range.add(c);
}
boolean addRestriction(OwlClass r) { return restrictions.add(r); }
public void setURI(IRI uri) { this.uri = uri; }
void setTransitive() { transitive = true; }
void setSymmetric() { symmetric = true; }
void setAsymmetric() { asymmetric = true; }
void setFunctional() { functional = true; }
void setInverseFunctional() { inverseFunctional = true; }
void setIrreflexive() { irreflexive = true; }
public IRI getURI() { return uri; }
public boolean isTransitive() { return transitive; }
public boolean isSymmetric() { return symmetric; }
public boolean isAsymmetric() { return asymmetric; }
public boolean isFunctional() { return functional; }
public boolean isInverseFunctional() { return inverseFunctional; }
public boolean isIrreflexive() { return irreflexive; }
/**
* Apply RL rule scm-spo: subPropertyOf transitivity.
* Follows subPropertyOf chains to compute all the ancestor properties.
* Assumes the hierarchy is small enough that a simple BFS is fine.
* @return Whether new information was discovered
*/
boolean computeSuperProperties() {
Set<OwlProperty> ancestors = new HashSet<OwlProperty>();
Set<OwlProperty> frontier = new HashSet<OwlProperty>(superProperties);
while (!frontier.isEmpty()) {
Set<OwlProperty> next = new HashSet<OwlProperty>();
for (OwlProperty ancestor : frontier) {
// This node is an ancestor; it's parents should be explored next
ancestors.add(ancestor);
next.addAll(ancestor.superProperties);
}
// Don't revisit nodes
next.removeAll(ancestors);
frontier = next;
}
boolean newInfo = !ancestors.equals(superProperties);
superProperties = ancestors;
return newInfo;
}
/**
* Infer domain and range from parents' domains and ranges. Only looks at
* those superproperties that are explicitly known; computeSuperProperties
* should generally be called first to get the full closure.
*/
void inheritDomainRange() {
//RL rule scm-dom2: (p2 domain c) && (p1 subPropertyOf p2) -> (p1 domain c)
//RL rule scm-rng2: (p2 range c) && (p1 subPropertyOf p2) -> (p1 range c)
for (OwlProperty ancestorProperty : superProperties) {
for (OwlClass c : ancestorProperty.domain) {
addDomain(c);
}
for (OwlClass c : ancestorProperty.range) {
addRange(c);
}
}
//RL rule scm-dom1: (p domain c1) && (c1 subClassOf c2) -> (p domain c2)
for (OwlClass domainClass : new HashSet<OwlClass>(this.domain)) {
domainClass.inheritDomains(this);
}
//RL rule scm-rng1: (p range c1) && (c1 subClassOf c2) -> (p range c2)
for (OwlClass rangeClass : new HashSet<OwlClass>(this.range)) {
rangeClass.inheritRanges(this);
}
}
/**
* Get all the superproperties of this subproperty.
*/
public Set<IRI> getSuperProperties() {
Set<IRI> ancestors = new HashSet<>();
for (OwlProperty ancestor : superProperties) {
ancestors.add(ancestor.uri);
}
// RL rules scm-op & scm-dp: Every property is a subproperty of itself
ancestors.add(this.uri);
return ancestors;
}
/**
* Get all the equivalent properties for this property.
* Apply RL rules scm-op and scm-dp: Every property is its own equivalent.
*/
public Set<IRI> getEquivalentProperties() {
Set<IRI> equivalents = new HashSet<>();
for (OwlProperty other : superProperties) {
if (other.superProperties.contains(this)) {
equivalents.add(other.uri);
}
}
// RL rules scm-op & scm-dp: Every property is equivalent to itself
equivalents.add(this.uri);
return equivalents;
}
/**
* Get all properties declared disjoint with this one.
*/
public Set<IRI> getDisjointProperties() {
Set<IRI> disjoint = new HashSet<>();
for (OwlProperty other : disjointProperties) {
disjoint.add(other.uri);
}
return disjoint;
}
/**
* Get all properties declared inverse of this one.
*/
public Set<IRI> getInverseProperties() {
Set<IRI> inverse = new HashSet<>();
for (OwlProperty other : inverseProperties) {
inverse.add(other.uri);
}
return inverse;
}
/**
* Get the domain (set of class URIs/Resources).
*/
public Set<Resource> getDomain() {
Set<Resource> domain = new HashSet<>();
for (OwlClass d : this.domain) {
domain.add(d.getURI());
}
return domain;
}
/**
* Get the range (set of class URIs/Resources).
*/
public Set<Resource> getRange() {
Set<Resource> range = new HashSet<>();
for (OwlClass r : this.range) {
range.add(r.getURI());
}
return range;
}
/**
* Get the property restrictions relevant to this property.
*/
public Set<Resource> getRestrictions() {
Set<Resource> restrictions = new HashSet<>();
for (OwlClass pr : this.restrictions) {
restrictions.add(pr.getURI());
}
return restrictions;
}
/**
* Apply property restriction rules that involve the same property.
* RL rule scm-svf1: [someValuesFrom sub] subClassOf [someValuesFrom super]
* RL rule scm-avf1: [allValuesFrom sub] subClassOf [allValuesFrom super]
* Doesn't check subclass transitivity.
* @return Whether any information was generated
*/
public boolean compareRestrictions() {
boolean newInfo = false;
for (OwlClass c1 : restrictions) {
// Get all superclasses of this restriction's avf target class(es)
// and avf target classes.
Set<Resource> avfSuperClasses = c1.getAvfSuperClasses();
Set<Resource> svfSuperClasses = c1.getSvfSuperClasses();
if (avfSuperClasses.isEmpty() && svfSuperClasses.isEmpty()) {
continue;
}
// Compare each other restriction's avf and svf targets to the
// appropriate set of superclasses.
for (OwlClass c2 : restrictions) {
//svm-svf1, svm-avf1
if (c1 != c2) {
Set<Resource> avf2 = c2.allValuesFrom();
avf2.retainAll(avfSuperClasses);
if (avf2.isEmpty()) {
Set<Resource> svf2 = c2.someValuesFrom();
svf2.retainAll(svfSuperClasses);
if (svf2.isEmpty()) {
continue;
}
}
// If c2's target is one of the parent classes of c1's
// target, then c2 is a more general version of c1 and
// therefore a superclass of c1.
newInfo = c1.addSuperClass(c2) || newInfo;
}
}
}
return newInfo;
}
}