/*******************************************************************************
* Copyright (c) 2009, 2010 Fraunhofer IWU and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Fraunhofer IWU - initial API and implementation
*******************************************************************************/
package net.enilink.komma.em.concepts;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import net.enilink.composition.traits.Behaviour;
import net.enilink.commons.iterator.Filter;
import net.enilink.commons.iterator.IExtendedIterator;
import net.enilink.commons.iterator.WrappedIterator;
import net.enilink.vocab.komma.KOMMA;
import net.enilink.vocab.owl.DatatypeProperty;
import net.enilink.vocab.owl.FunctionalProperty;
import net.enilink.komma.core.IEntity;
import net.enilink.komma.core.IQuery;
import net.enilink.komma.core.IReference;
import net.enilink.komma.em.util.KommaUtil;
public abstract class PropertySupport extends BehaviorBase implements
IProperty, Behaviour<IProperty> {
private static final String SELECT_DIRECT_SUBPROPERTIES(boolean named) {
return PREFIX
+ "SELECT DISTINCT ?subProperty "
+ "WHERE { "
+ "?subProperty rdfs:subPropertyOf ?superProperty . "
+ "FILTER NOT EXISTS {"
+ "?subProperty rdfs:subPropertyOf ?otherSuperProperty . "
+ "?otherSuperProperty rdfs:subPropertyOf ?superProperty . "
+ "FILTER (?subProperty != ?otherSuperProperty && ?superProperty != ?otherSuperProperty)"
+ "} " //
+ "FILTER (?subProperty != ?superProperty"
+ (named ? " && isIRI(?subProperty)" : "") + ") }";
};
private static final String SELECT_SUBPROPERTIES(boolean named) {
return PREFIX + "SELECT DISTINCT ?subProperty " + "WHERE { "
+ "?subProperty rdfs:subPropertyOf ?superProperty . "
+ "FILTER (?subProperty != ?superProperty"
+ (named ? "&& isIRI(?subProperty)" : "") + ") }";
};
private static final String SELECT_DIRECT_SUPERPROPERTIES(boolean named) {
return PREFIX
+ "SELECT DISTINCT ?superProperty "
+ "WHERE { "
+ "?subProperty rdfs:subPropertyOf ?superProperty . "
+ "FILTER NOT EXISTS {"
+ "?subProperty rdfs:subPropertyOf ?otherSuperProperty . "
+ "?otherSuperProperty rdfs:subPropertyOf ?superProperty . "
+ "FILTER (?subProperty != ?otherSuperProperty && ?superProperty != ?otherSuperProperty)"
+ "} " + "FILTER (?subProperty != ?superProperty"
+ (named ? " && isIRI(?subProperty)" : "") + ")" //
+ "}";
};
private static final String SELECT_SUPERPROPERTIES(boolean named) {
return PREFIX + "SELECT DISTINCT ?superProperty " + "WHERE { "
+ "?subProperty rdfs:subPropertyOf ?superProperty . "
+ "FILTER (?subProperty != ?superProperty"
+ (named ? "&& isIRI(?subProperty)" : "") + ") }";
}
private static final String IS_CONTAINMENT = PREFIX + "ASK { " //
+ "?property rdfs:subPropertyOf komma:contains" //
+ " }";
private static final String IS_ORDERED_CONTAINMENT = PREFIX + "ASK { " //
+ "{ ?property rdfs:subPropertyOf komma:orderedContains }" //
+ " }";
private static final String IS_RANGE_INSTANCE = PREFIX
+ "ASK { ?object a ?class . " //
+ " { ?property rdfs:range ?class } UNION { ?class rdfs:subClassOf ?superClass . ?property rdfs:range ?superClass }" //
+ " }";
private static final String IS_DOMAIN_INSTANCE = PREFIX + "ASK { " //
+ "?object a ?domain ." //
+ "?property rdfs:domain ?domain " //
+ " }";
private static final String HAS_LIST_RANGE_QUERY = PREFIX + "ASK { " //
+ "{ ?property rdfs:range rdf:List } UNION " //
+ "{ ?property rdfs:range rdfs:Container }" //
+ " }";
private static final String DIRECT_RANGE_QUERY = PREFIX
+ "SELECT DISTINCT ?range WHERE { " //
+ "?property rdfs:range ?range " //
+ "FILTER NOT EXISTS { ?property rdfs:range ?otherRange . ?range rdfs:subClassOf ?otherRange FILTER (?range != ?otherRange) }" //
+ " }";
private static final String RANGE_QUERY = PREFIX
+ "SELECT DISTINCT ?range WHERE { " //
+ "?property rdfs:range ?range " //
+ " }";
@Override
public boolean isContainment() {
if (getBehaviourDelegate() instanceof DatatypeProperty) {
return false;
}
if (KOMMA.PROPERTY_CONTAINS.equals(getURI())) {
return true;
}
IQuery<?> query = getEntityManager().createQuery(IS_CONTAINMENT);
query.setParameter("property", this);
return query.getBooleanResult();
}
@SuppressWarnings("unchecked")
protected IExtendedIterator<IProperty> getSubProperties(boolean direct,
boolean includeInferred) {
IQuery<?> query = getEntityManager().createQuery(
direct ? SELECT_DIRECT_SUBPROPERTIES(true)
: SELECT_SUBPROPERTIES(true), includeInferred);
query.setParameter("superProperty", this);
return (IExtendedIterator<IProperty>) query.evaluate();
}
@Override
public IExtendedIterator<IProperty> getDirectSuperProperties() {
return getSuperProperties(true, true);
}
@Override
public IExtendedIterator<IProperty> getDirectSubProperties() {
// [PERFORMANCE] direct sub-properties are retrieved without inference
return getSubProperties(true, false);
}
@Override
public IExtendedIterator<IProperty> getSubProperties() {
return getSubProperties(true, true);
}
@Override
public IExtendedIterator<IProperty> getSuperProperties() {
return getSuperProperties(true, true);
}
@SuppressWarnings("unchecked")
protected IExtendedIterator<IProperty> getSuperProperties(boolean direct,
boolean includeInferred) {
IQuery<?> query = getEntityManager().createQuery(
direct ? SELECT_DIRECT_SUPERPROPERTIES(true)
: SELECT_SUPERPROPERTIES(true), includeInferred);
query.setParameter("subProperty", this);
return (IExtendedIterator<IProperty>) query.evaluate();
}
@Override
public boolean isOrderedContainment() {
if (getBehaviourDelegate() instanceof DatatypeProperty) {
return false;
}
if (KOMMA.PROPERTY_ORDEREDCONTAINS.equals(getURI())) {
return true;
}
IQuery<?> query = getEntityManager()
.createQuery(IS_ORDERED_CONTAINMENT);
query.setParameter("property", this);
return query.getBooleanResult();
}
@Override
public boolean isDomainCompatible(Object object) {
if (object instanceof IReference) {
IQuery<?> query = getEntityManager()
.createQuery(IS_DOMAIN_INSTANCE);
query.setParameter("property", this);
query.setParameter("object", object);
return query.getBooleanResult();
}
return false;
}
@Override
public boolean isRangeCompatible(IResource subject, Object object) {
if (object instanceof IResource) {
// query can be optimized if OWL-inferencing is supported
if (getEntityManager().getInferencing().doesOWL()) {
String query = PREFIX
+ "ASK { ?o a ?c { ?p rdfs:range ?c }" //
+ " UNION { ?c owl:onProperty ?p { ?restriction owl:allValuesFrom ?c } UNION { ?restriction owl:someValuesFrom ?c }}" //
+ "}";
return subject.getEntityManager().createQuery(query)
.setParameter("o", object).setParameter("p", this)
.getBooleanResult()
|| getRdfsRanges().isEmpty();
}
String query = PREFIX
+ "SELECT DISTINCT ?r WHERE {"
+ "?s a [ rdfs:subClassOf* ?restriction ] . ?restriction owl:onProperty [ rdfs:subPropertyOf* ?p ] . "
+ "{{ ?restriction owl:allValuesFrom ?someClass } UNION { ?restriction owl:someValuesFrom ?someClass } FILTER NOT EXISTS { ?someClass owl:intersectionOf ?x }} ."
+ "?r rdfs:subClassOf ?someClass"
+ "}";
@SuppressWarnings("unchecked")
IExtendedIterator<? extends IClass> it = (IExtendedIterator<IClass>) subject
.getEntityManager().createQuery(query)
.setParameter("s", subject).setParameter("p", this)
.evaluate();
if (it.hasNext()) {
Set<IClass> rangeClasses = new HashSet<IClass>();
Set<IReference> seenClasses = new HashSet<IReference>();
Queue<net.enilink.vocab.owl.Class> queue = new LinkedList<net.enilink.vocab.owl.Class>(
it.toSet());
while (!queue.isEmpty()) {
net.enilink.vocab.owl.Class clazz = queue.remove();
if (!seenClasses.add(clazz)) {
continue;
}
List<net.enilink.vocab.owl.Class> unionOf = clazz
.getOwlUnionOf();
if (unionOf == null) {
List<net.enilink.vocab.owl.Class> intersectionOf = clazz
.getOwlIntersectionOf();
if (intersectionOf == null && clazz.getURI() != null) {
rangeClasses.add((IClass) clazz);
}
} else {
queue.addAll(unionOf);
}
}
// TODO Is this really required?
KommaUtil.removeSuperClasses(rangeClasses);
for (IReference typeClass : new ArrayList<>(((IResource) object).getRdfTypes())) {
if (rangeClasses.contains(typeClass)) {
return true;
}
}
return false;
}
it.close();
}
return isRangeCompatible(object);
}
@Override
public boolean isRangeCompatible(Object object) {
if (getBehaviourDelegate() instanceof DatatypeProperty) {
// TODO further tests in this case
return true;
}
if (object instanceof IReference) {
IQuery<?> query = getEntityManager().createQuery(IS_RANGE_INSTANCE);
query.setParameter("property", this);
query.setParameter("object", object);
return query.getBooleanResult() || getRdfsRanges().isEmpty();
}
return false;
}
@Override
public IExtendedIterator<? extends IClass> getNamedRanges(IEntity subject,
boolean direct) {
IExtendedIterator<? extends IClass> it = null;
// query can be optimized if OWL-inferencing is supported
if (getEntityManager().getInferencing().doesOWL()) {
String var = direct ? "r" : "resultR";
String query = PREFIX
+ "SELECT DISTINCT ?"
+ var
+ " WHERE {"
+ " ?o a ?restriction . ?restriction owl:onProperty ?p ."
+ " { ?restriction owl:allValuesFrom ?r } UNION { ?restriction owl:someValuesFrom ?r } ."
+ " FILTER NOT EXISTS {"
+ " ?subP rdfs:subPropertyOf ?p ."
+ " ?otherRestriction owl:onProperty ?subP ."
+ " ?o a ?otherRestriction ."
+ " { ?otherRestriction owl:allValuesFrom ?otherR } ."
+ " ?otherR rdfs:subClassOf ?r . " //
+ " FILTER (isIRI(?otherR) && (?subP != ?p || ?otherR != ?r))" //
+ " }" //
+ (direct ? "" : " ?resultR rdfs:subClassOf ?r . ")
+ " FILTER (isIRI(?" + var + ") && ?" + var
+ " != owl:Nothing)" //
+ " FILTER NOT EXISTS {" //
+ " ?" + var + " komma:isAbstract ?abstract" //
+ " }" //
+ "}";
it = subject.getEntityManager().createQuery(query)
.setParameter("o", subject).setParameter("p", this)
.evaluate(IClass.class);
if (it.hasNext()) {
return it;
}
}
if (it != null) {
it.close();
}
String query = PREFIX
+ "SELECT DISTINCT ?r WHERE {"
+ "?o a ?c . ?c rdfs:subClassOf ?restriction . ?restriction owl:onProperty ?p . "
+ "{{ ?restriction owl:allValuesFrom ?r } UNION { ?restriction owl:someValuesFrom ?r } FILTER NOT EXISTS { ?r owl:intersectionOf ?x } }"
+ "}";
it = subject.getEntityManager().createQuery(query)
.setParameter("o", subject).setParameter("p", this)
.evaluate(IClass.class);
if (it.hasNext()) {
Set<IClass> namedRangeClasses = new HashSet<IClass>();
Set<IReference> seenClasses = new HashSet<IReference>();
Queue<net.enilink.vocab.owl.Class> queue = new LinkedList<net.enilink.vocab.owl.Class>(
it.toSet());
while (!queue.isEmpty()) {
net.enilink.vocab.owl.Class clazz = queue.remove();
if (!seenClasses.add(clazz)) {
continue;
}
List<net.enilink.vocab.owl.Class> unionOf = clazz
.getOwlUnionOf();
if (unionOf == null) {
List<net.enilink.vocab.owl.Class> intersectionOf = clazz
.getOwlIntersectionOf();
if (intersectionOf == null && clazz.getURI() != null) {
namedRangeClasses.add((IClass) clazz);
}
} else {
queue.addAll(unionOf);
}
}
KommaUtil.removeSuperClasses(namedRangeClasses);
if (!direct) {
KommaUtil.unfoldSubClasses(namedRangeClasses);
}
return WrappedIterator.create(namedRangeClasses.iterator());
}
it.close();
return getRanges(direct).filterDrop(new Filter<IClass>() {
@Override
public boolean accept(IClass c) {
return c.getURI() == null;
}
});
}
@Override
public boolean hasListRange() {
if (getBehaviourDelegate() instanceof DatatypeProperty) {
return false;
}
IQuery<?> query = getEntityManager().createQuery(HAS_LIST_RANGE_QUERY);
query.setParameter("property", this);
return query.getBooleanResult();
}
@Override
public IExtendedIterator<? extends IClass> getRanges(boolean direct) {
return getEntityManager()
.createQuery(direct ? DIRECT_RANGE_QUERY : RANGE_QUERY)
.setParameter("property", this).evaluate(IClass.class);
}
public boolean isMany(IReference subject) {
if (getBehaviourDelegate() instanceof FunctionalProperty) {
return hasListRange();
}
if (subject != null) {
if (((IResource) getEntityManager().find(subject))
.getApplicableCardinality(getBehaviourDelegate())
.getSecond() <= 1) {
return hasListRange();
}
}
return true;
}
}