io.konig.shacl.LogicalShapeBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of konig-core Show documentation
Show all versions of konig-core Show documentation
A library for core classes (Graph, Vertex, Edge, etc.)
package io.konig.shacl;
/*
* #%L
* konig-shacl
* %%
* Copyright (C) 2015 - 2016 Gregory McFall
* %%
* Licensed 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.
* #L%
*/
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.openrdf.model.Resource;
import org.openrdf.model.URI;
import io.konig.core.AmbiguousPreferredClassException;
import io.konig.core.OwlReasoner;
import io.konig.core.Vertex;
public class LogicalShapeBuilder {
private OwlReasoner reasoner;
private LogicalShapeNamer shapeNamer;
private Set stack;
public LogicalShapeBuilder(OwlReasoner reasoner, LogicalShapeNamer shapeNamer) {
this.reasoner = reasoner;
this.shapeNamer = shapeNamer;
}
private URI targetClass(Shape shape) {
URI targetClass = shape.getTargetClass();
try {
return targetClass == null ? null : reasoner.preferredClassAsURI(targetClass);
} catch (AmbiguousPreferredClassException e) {
throw new RuntimeException(e);
}
}
public void buildLogicalShapes(ShapeManager shapeManager, ClassManager classManager) {
List list = shapeManager.listShapes();
stack = new HashSet<>();
for (Shape shape : list) {
URI targetClass = targetClass(shape);
Shape logicalShape = classManager.getLogicalShape(targetClass);
if (logicalShape == null) {
buildLogicalShape(list, targetClass, classManager);
}
}
List classList = reasoner.owlClassList();
for (Vertex v : classList) {
Resource id = v.getId();
if (id instanceof URI) {
URI uri = (URI) id;
Shape logicalShape = classManager.getLogicalShape(uri);
if (logicalShape == null) {
buildLogicalShape(null, uri, classManager);
}
}
}
for (URI owlClass : stack) {
Shape prior = classManager.getLogicalShape(owlClass);
if (prior == null) {
URI shapeId = shapeNamer.logicalShapeForOwlClass(owlClass);
Shape shape = new Shape(shapeId);
shape.setTargetClass(owlClass);
classManager.addLogicalShape(shape);
}
}
stack = null;
}
private void buildLogicalShape(List list, URI targetClass, ClassManager classManager) {
if (targetClass == null) {
return;
}
URI shapeId = shapeNamer.logicalShapeForOwlClass(targetClass);
Shape shape = new Shape(shapeId);
shape.setTargetClass(targetClass);
classManager.addLogicalShape(shape);
List filter = filterByTargetClass(list, targetClass);
Map> map = buildPropertyMap(filter);
for (List pList : map.values()) {
mergeProperties(shape, pList);
}
}
private Resource preferredClass(Resource owlClass) {
try {
return owlClass==null ? null : reasoner.preferredClass(owlClass).getId();
} catch (AmbiguousPreferredClassException e) {
throw new RuntimeException(e);
}
}
private void mergeProperties(Shape shape, List pList) {
PropertyConstraint constraint = null;
for (PropertyConstraint p : pList) {
if (constraint == null) {
constraint = new PropertyConstraint(p.getPredicate());
shape.add(constraint);
}
Integer minCountA = constraint.getMinCount();
Integer minCountB = p.getMinCount();
if (minCountA == null) {
constraint.setMinCount(minCountB);
} else if (minCountB == null || (minCountB < minCountA) ) {
constraint.setMinCount(minCountB);
}
Integer maxCountA = constraint.getMaxCount();
Integer maxCountB = p.getMaxCount();
if (maxCountA==null) {
constraint.setMaxCount(maxCountB);
} else if (maxCountB==null || (maxCountB>maxCountA)) {
constraint.setMaxCount(maxCountB);
}
Resource datatypeA = preferredClass(constraint.getDatatype());
Resource datatypeB = preferredClass(p.getDatatype());
Resource classA = preferredClass(constraint.getValueClass());
Resource classB = preferredClass(p.getValueClass());
Shape valueShapeA = constraint.getShape();
Shape valueShapeB = p.getShape();
if (valueShapeB!=null) {
classB = leastUpperBound(shape, p, "sh:class", targetClass(valueShapeB), classB);
}
if (valueShapeA!=null && classA==null) {
classA = valueShapeA.getTargetClass();
}
if (valueShapeA!=null && classA != null) {
classA = leastUpperBound(shape, p, "sh:class", targetClass(valueShapeA), classA);
}
classB = leastUpperBound(shape, p, "sh:class", classB, classA);
datatypeB = (URI) leastUpperBound(shape, p, "sh:datatype", datatypeB, datatypeA);
if (classB instanceof URI) {
stack.add((URI)classB);
}
constraint.setValueClass(classB);
constraint.setDatatype((URI) datatypeB);
}
}
private Resource leastUpperBound(Shape shape, PropertyConstraint p, String context, Resource classA, Resource classB) {
if (classA!=null && classB==null) {
return classA;
}
if (classB!=null && classA==null) {
return classB;
}
if (classA != null && classB != null) {
if (classA.equals(classB)) {
return classA;
}
// TODO: compute least upper bound via semantic reasoning
throw new RuntimeException("Conflicting " + context + " detected on property " +
p.getPredicate().getLocalName() + " of " +
shape.getTargetClass().getLocalName());
}
return null;
}
private Map> buildPropertyMap(List list) {
Map> map = new HashMap<>();
for (Shape shape : list) {
List pList = shape.getProperty();
for (PropertyConstraint p : pList) {
URI predicate = p.getPredicate();
List sink = map.get(predicate.stringValue());
if (sink == null) {
sink = new ArrayList<>();
map.put(predicate.stringValue(), sink);
}
sink.add(p);
}
}
return map;
}
private List filterByTargetClass(List list, URI targetClass) {
List result = new ArrayList<>();
if (list != null) {
for (Shape shape : list) {
if (targetClass.equals(targetClass(shape))) {
result.add(shape);
}
}
}
return result;
}
}