io.konig.core.showl.ShowlManager 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.core.showl;
import java.text.MessageFormat;
/*
* #%L
* Konig Core
* %%
* Copyright (C) 2015 - 2018 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.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.openrdf.model.Namespace;
import org.openrdf.model.Resource;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.model.vocabulary.OWL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.konig.core.Context;
import io.konig.core.Graph;
import io.konig.core.NamespaceManager;
import io.konig.core.OwlReasoner;
import io.konig.core.Vertex;
import io.konig.core.impl.BasicContext;
import io.konig.core.impl.RdfUtil;
import io.konig.core.showl.expression.ShowlExpressionBuilder;
import io.konig.core.util.IriTemplate;
import io.konig.core.util.ValueFormat;
import io.konig.core.vocab.Konig;
import io.konig.datasource.DataSource;
import io.konig.formula.Direction;
import io.konig.formula.DirectionStep;
import io.konig.formula.Expression;
import io.konig.formula.Formula;
import io.konig.formula.FormulaVisitor;
import io.konig.formula.HasPathStep;
import io.konig.formula.IriValue;
import io.konig.formula.LiteralFormula;
import io.konig.formula.PathExpression;
import io.konig.formula.PathStep;
import io.konig.formula.PathTerm;
import io.konig.formula.PredicateObjectList;
import io.konig.formula.PrimaryExpression;
import io.konig.formula.QuantifiedExpression;
import io.konig.formula.VariableTerm;
import io.konig.shacl.NodeKind;
import io.konig.shacl.PropertyConstraint;
import io.konig.shacl.Shape;
import io.konig.shacl.ShapeManager;
public class ShowlManager implements ShowlClassManager {
private static final Logger logger = LoggerFactory.getLogger(ShowlManager.class);
private Map nodeShapes = new LinkedHashMap<>();
private Map owlClasses = new LinkedHashMap<>();
private Map properties = new LinkedHashMap<>();
private ShowlNodeShapeSet emptySet = null;
private OwlReasoner reasoner;
private ShowlNodeShapeConsumer consumer;
private ShapeManager shapeManager;
private List classlessShapes;
private Set nodeMappings = null;
private ShowlTargetNodeSelector targetNodeSelector;
private ShowlSourceNodeSelector sourceNodeSelector;
private Map enumMappingActions;
private ShowlService showlFactory;
private ShowlMappingStrategy mappingStrategy;
private StaticDataSource staticDataSource = null;
private Set consumable;
public ShowlManager(ShapeManager shapeManager, OwlReasoner reasoner) {
this(shapeManager, reasoner, new ShowlRootTargetClassSelector(), null);
}
public ShowlManager(
ShapeManager shapeManager,
OwlReasoner reasoner,
ShowlSourceNodeSelector sourceNodeSelector,
ShowlNodeShapeConsumer consumer
) {
this.shapeManager = shapeManager;
this.reasoner = reasoner;
this.sourceNodeSelector = sourceNodeSelector;
this.consumer = consumer;
this.showlFactory = new Factory();
}
public ShowlManager(
ShapeManager shapeManager,
OwlReasoner reasoner,
ShowlTargetNodeSelector targetNodeSelector,
ShowlSourceNodeSelector sourceNodeSelector,
ShowlNodeShapeConsumer consumer
) {
this.shapeManager = shapeManager;
this.reasoner = reasoner;
this.targetNodeSelector = targetNodeSelector;
this.sourceNodeSelector = sourceNodeSelector;
this.mappingStrategy = new RankedSourceMappingStrategy();
this.consumer = consumer;
this.showlFactory = new Factory();
}
public ShapeManager getShapeManager() {
return shapeManager;
}
public ShowlService getShowlFactory() {
return showlFactory;
}
public void load() throws ShowlProcessingException {
clear();
loadShapes();
inferTargetClasses();
inferInverses();
buildJoinConditions();
}
public ShowlTargetNodeSelector getTargetNodeSelector() {
return targetNodeSelector;
}
public void setTargetNodeSelector(ShowlTargetNodeSelector targetNodeSelector) {
this.targetNodeSelector = targetNodeSelector;
}
public ShowlSourceNodeSelector getSourceNodeSelector() {
return sourceNodeSelector;
}
public void setSourceNodeSelector(ShowlSourceNodeSelector sourceNodeSelector) {
this.sourceNodeSelector = sourceNodeSelector;
}
public Set listNodeShapeIds() {
return nodeShapes.keySet();
}
public ShowlNodeShape getNodeShape(Resource shapeId) {
return nodeShapes.get(shapeId).top();
}
public ShowlNodeShapeSet getNodeShapeSet(Resource shapeId) {
ShowlNodeShapeSet set = nodeShapes.get(shapeId);
return (set == null) ? emptySet() : set;
}
public ShowlProperty getProperty(URI propertyId) {
return properties.get(propertyId);
}
public Collection getProperties() {
return properties.values();
}
private ShowlNodeShapeSet emptySet() {
if (emptySet==null) {
emptySet = new ShowlNodeShapeSet();
}
return emptySet;
}
public OwlReasoner getReasoner() {
return reasoner;
}
public void clear() {
nodeMappings = null;
classlessShapes = null;
}
protected void inferInverses() {
List list = new ArrayList<>(getProperties());
for (ShowlProperty p : list) {
if (p.getInverses().isEmpty()) {
inferInverse(p);
}
}
}
private void inferInverse(ShowlProperty p) {
URI predicate = p.getPredicate();
Set set = reasoner.inverseOf(predicate);
for (URI other : set) {
p.addInverse(produceShowlProperty(other));
}
for (ShowlPropertyShape q : p.getPropertyShapes()) {
ShowlPropertyShape peer = q.getPeer();
if (peer != null) {
if (peer.getDirection() != q.getDirection()) {
p.addInverse(peer.getProperty());
}
}
}
}
private void buildJoinConditions() throws ShowlProcessingException {
nodeMappings = new LinkedHashSet<>();
enumMappingActions = new HashMap<>();
consumable = null;
buildTransformExpressions();
// TODO: remove the body of this method and use buildTransformExpressions only
// for (Shape shape : shapeManager.listShapes()) {
// if (!shape.getShapeDataSource().isEmpty()) {
// for (ShowlNodeShape node : getNodeShape(shape.getId())) {
// buildJoinConditions(node, MappingRole.TARGET, null, consumer);
// }
// }
//
// }
//
// for (EnumMappingAction action : enumMappingActions.values()) {
// action.execute();
// }
//
if (consumable != null) {
for (ShowlNodeShape node : consumable) {
consumer.consume(node);
}
}
consumable = null;
nodeMappings = null;
enumMappingActions = null;
}
private void buildTransformExpressions() {
for (Shape shape : shapeManager.listShapes()) {
List targetList = targetNodeSelector.produceTargetNodes(showlFactory, shape);
for (ShowlNodeShape targetNode : targetList) {
buildTransform(targetNode);
if (mappingStrategy != null) {
Set unmapped = mappingStrategy.selectMappings(this, targetNode);
if (unmapped.isEmpty()) {
if (consumer != null) {
consumer.consume(targetNode);
}
} else {
// TODO: Seek other source NodeShapes that may be joined to the selected shapes
// For now, just log the missing shapes
logger.warn("Failed to map the following properties:...");
for (ShowlPropertyShape p : unmapped) {
logger.warn(" {}", p.getPath());
}
}
}
}
}
}
private void buildTransform(ShowlNodeShape targetNode) {
Set candidates = selectCandidateSources(targetNode);
addSourcesForVariables(candidates, targetNode);
if (candidates.isEmpty()) {
if (logger.isWarnEnabled()) {
logger.warn("buildTransform: Cannot build transform for {}. No candidate sources were found.",
targetNode.getPath());
}
}
buildTransforms(targetNode, candidates);
}
private Set selectCandidateSources(ShowlNodeShape targetNode) {
Set set = sourceNodeSelector.selectCandidateSources(showlFactory, targetNode);
for (ShowlNodeShape sourceNode : set) {
if (sourceNode.getTargetNode()==null) {
sourceNode.setTargetNode(targetNode);
}
}
return set;
}
private void addSourcesForVariables(Set candidates, ShowlNodeShape targetNode) {
for (PropertyConstraint c : targetNode.getShape().getVariable()) {
URI predicate = c.getPredicate();
ShowlPropertyShape p = targetNode.findOut(predicate);
if (p != null && p.getValueShape()!=null) {
Set set = selectCandidateSources(p.getValueShape());
candidates.addAll(set);
}
}
}
private void buildTransforms(ShowlNodeShape defaultTargetNode, Collection candidates) {
for (ShowlNodeShape sourceNode : candidates) {
ShowlNodeShape targetNode = sourceNode.getTargetNode();
if (targetNode == null) {
targetNode = defaultTargetNode;
}
if (logger.isTraceEnabled()) {
logger.trace("buildTransforms: From source {} to target {}", sourceNode.getPath(), targetNode.getPath());
}
for (ShowlDirectPropertyShape targetProperty : targetNode.getProperties()) {
ShowlExpression formula = targetProperty.getFormula();
if (formula == null) {
matchProperty(sourceNode, targetProperty);
} else {
Set set = new LinkedHashSet<>();
formula.addProperties(set);
for (ShowlPropertyShape arg : set) {
matchProperty(sourceNode, arg);
}
}
}
}
// Add mappings to static enum values
for (ShowlDirectPropertyShape direct : defaultTargetNode.getProperties()) {
ShowlNodeShape valueShape = direct.getValueShape();
if (valueShape != null) {
ShowlClass owlClass = valueShape.getOwlClass();
if (owlClass != null && reasoner.isEnumerationClass(owlClass.getId())) {
addEnumMappings(valueShape);
}
}
}
}
private void addEnumMappings(ShowlNodeShape targetShape) {
// We need two NodeShape instances for an enumeration.
// The globalEnumShape keeps track of all properties of the Enumeration class that are referenced.
// This is necessary because the same enumeration might be referenced at more than one point within
// the transform. It's unlikely, but possible so we need to handle that case.
// The localEnumShape keeps track of the properties of the Enumeration class that are referenced by
// the given targetShape.
ShowlNodeShape globalEnumShape = showlFactory.logicalNodeShape(targetShape.getOwlClass().getId());
ShowlNodeShape localEnumShape = localEnumShape(globalEnumShape, targetShape);
localEnumShape.setLogicalNodeShape(globalEnumShape);
for (ShowlDirectPropertyShape direct : targetShape.getProperties()) {
// For now, we assume that every property of the enumTargetShape is available statically within
// the background graph.
// We may need to back off that assumption later and consider synonyms.
// Add the property from the target shape to both the global and the local Enumeration NodeShape.
// In the case of the globalEnumShape, this call will do nothing if the property already exists.
directProperty(globalEnumShape, direct.getProperty());
ShowlDirectPropertyShape localEnumProperty = directProperty(localEnumShape, direct.getProperty());
direct.addExpression(new ShowlEnumPropertyExpression(localEnumProperty));
if (logger.isTraceEnabled()) {
logger.trace("addEnumMapping: {}...{}", localEnumProperty.getPath(), direct.getPath());
}
}
}
private ShowlNodeShape localEnumShape(ShowlNodeShape globalEnumShape, ShowlNodeShape targetShape) {
ShowlNodeShape local = new ShowlNodeShape(this, null, globalEnumShape.getShape(), globalEnumShape.getOwlClass());
globalEnumShape.getOwlClass().addTargetClassOf(local);
local.setTargetProperty(targetShape.getAccessor());
local.setShapeDataSource(new ShowlDataSource(local, staticDataSource()));
return local;
}
private DataSource staticDataSource() {
if (staticDataSource == null) {
staticDataSource = new StaticDataSource();
}
return staticDataSource;
}
private ShowlDirectPropertyShape directProperty(ShowlNodeShape enumShape, ShowlProperty property) {
URI predicate = property.getPredicate();
ShowlDirectPropertyShape direct = enumShape.getProperty(predicate);
if (direct == null) {
direct = createDirectPropertyShape(enumShape, property, null);
enumShape.addProperty(direct);
}
return direct;
}
private boolean matchProperty(ShowlNodeShape sourceNode, ShowlPropertyShape targetProperty) {
if (targetProperty == null) {
return false;
}
URI targetPredicate = targetProperty.getPredicate();
ShowlPropertyShape sourceProperty = sourceNode.getProperty(targetPredicate);
if (match(sourceProperty, targetProperty)) {
return true;
}
// Check for a derived property in the source node with the same predicate as the target property.
ShowlDerivedPropertyList derivedList = sourceNode.getDerivedProperty(targetPredicate);
sourceProperty = derivedList==null ? null : derivedList.unfiltered();
if (match(sourceProperty, targetProperty)) {
return true;
}
return false;
}
private boolean match(ShowlPropertyShape sourceProperty, ShowlPropertyShape targetProperty) {
if (sourceProperty != null) {
if (!(sourceProperty instanceof ShowlDirectPropertyShape)) {
ShowlPropertyShape synonym = sourceProperty.getSynonym();
if (synonym instanceof ShowlDirectPropertyShape) {
sourceProperty = synonym;
}
}
if (!(targetProperty instanceof ShowlDirectPropertyShape)) {
ShowlPropertyShape synonym = targetProperty.getSynonym();
if (synonym instanceof ShowlDirectPropertyShape) {
targetProperty = synonym;
}
}
addExpression(targetProperty, sourceProperty);
return true;
}
return false;
}
private void buildJoinConditions(ShowlNodeShape nodeA, MappingRole role, ShowlJoinCondition joinCondition, ShowlNodeShapeConsumer consumer) throws ShowlProcessingException {
if (nodeA.getShape().getNodeShapeCube() != null) {
joinCube(nodeA, joinCondition);
addConsumable(nodeA);
} else if (nodeA.getAccessor()==null && nodeA.hasDataSource() && !isUndefinedClass(nodeA.getOwlClass())) {
Set candidates = selectCandidateSources(nodeA);
for (ShowlNodeShape nodeB : candidates) {
if (nodeB.getShape() == nodeA.getShape()) {
// Identity mapping
ShowlNodeShape targetNode = role==MappingRole.TARGET ? nodeA : nodeB;
ShowlNodeShape sourceNode = role==MappingRole.TARGET ? nodeB : nodeA;
createIdentityMapping(sourceNode, targetNode, null);
continue;
}
ShowlClass classA = nodeA.getOwlClass();
ShowlClass classB = nodeB.getOwlClass();
ShowlNodeShape targetNode = role==MappingRole.TARGET ? nodeA : nodeB;
ShowlNodeShape sourceNode = role==MappingRole.TARGET ? nodeB : nodeA;
if (classA.isSubClassOf(classB) || classB.isSubClassOf(classA)) {
doJoin(targetNode, sourceNode, joinCondition);
} else {
joinNested(sourceNode, targetNode, joinCondition);
}
}
joinObjectProperties(nodeA, joinCondition);
if (consumer != null) {
addConsumable(nodeA);
}
}
// if (nodeA.isNamedRoot()) {
// ShowlClass owlClass = nodeA.getOwlClass();
// if (!isUndefinedClass(owlClass)) {
// for (ShowlNodeShape nodeB : owlClass.getTargetClassOf()) {
// if (nodeB == nodeA) {
// continue;
// }
// if (nodeB.isNamedRoot()) {
// // TODO: consider shapes that are not named roots
//
// ShowlNodeShape sourceNode = null;
// ShowlNodeShape targetNode = null;
// switch (role) {
// case SOURCE:
// sourceNode = nodeA;
// targetNode = nodeB;
// break;
//
// case TARGET:
// sourceNode = nodeB;
// targetNode = nodeA;
// }
//
// doJoin(targetNode, sourceNode, joinCondition);
//
// }
// }
// joinObjectProperties(nodeA, joinCondition);
// }
// }
}
private void joinCube(ShowlNodeShape targetNode, ShowlJoinCondition joinCondition) {
List varList = targetNode.getShape().getVariable();
if (varList.size() != 1) {
fail("Expected a single variable for NodeShape {0} associated with a cadl:Cube", targetNode.getPath());
}
PropertyConstraint p = varList.get(0);
URI valueType = RdfUtil.uri(p.getValueClass());
if (valueType == null) {
fail("sh:class of variable ?{0} on NodeShape {1} must be defined.",
p.getPredicate().getLocalName(),
RdfUtil.compactName(reasoner.getGraph().getNamespaceManager(), targetNode.getShape().getId()));
}
for (ShowlDerivedPropertyShape varProperty : targetNode.getDerivedProperty(p.getPredicate())) {
ShowlNodeShape varNode = varProperty.getValueShape();
if (varNode != null) {
Set candidates = selectCandidateSources(targetNode);
for (ShowlNodeShape sourceNode : candidates) {
List idList = sourceNode.out(Konig.id);
if (idList.isEmpty()) {
continue;
}
ShowlPropertyShape sourceId = idList.get(0);
ShowlTargetToSourceJoinCondition t2s = new ShowlTargetToSourceJoinCondition(varProperty, sourceId);
ShowlFromCondition fromCondition = new ShowlFromCondition(t2s, sourceNode);
for (ShowlPropertyShape targetOut : varNode.allOutwardProperties()) {
for (ShowlPropertyShape sourceOut : sourceNode.out(targetOut.getPredicate())) {
if (sourceOut instanceof ShowlDirectPropertyShape) {
produceMapping(fromCondition, sourceOut, targetOut);
}
}
}
}
}
}
}
private void fail(String pattern, Object... args) {
String text = MessageFormat.format(pattern, args);
throw new ShowlProcessingException(text);
}
private void createIdentityMapping(ShowlNodeShape sourceNode, ShowlNodeShape targetNode, ShowlJoinCondition join) {
if (join == null) {
join = new ShowlFromCondition(null, sourceNode);
}
for (ShowlDirectPropertyShape targetProperty : targetNode.getProperties()) {
ShowlDirectPropertyShape sourceProperty = sourceNode.getProperty(targetProperty.getPredicate());
new ShowlMapping(join, sourceProperty, targetProperty);
if (targetProperty.getValueShape() != null) {
createIdentityMapping(sourceProperty.getValueShape(), targetProperty.getValueShape(), join);
}
}
}
private void addConsumable(ShowlNodeShape nodeA) {
if (consumable == null) {
consumable = new HashSet<>();
}
consumable.add(nodeA);
if (logger.isTraceEnabled()) {
logger.trace("addConsumable({})", nodeA.getPath());
}
}
private void joinNested(ShowlNodeShape sourceNode, ShowlNodeShape targetNode, ShowlJoinCondition prior) {
if (logger.isTraceEnabled()) {
logger.trace("joinNested({}, {})", sourceNode.getPath(), targetNode.getPath());
}
ShowlClass targetClass = targetNode.getOwlClass();
ShowlNodeShape nestedSource = findNestedSource(sourceNode, targetClass);
if (nestedSource != null) {
doJoin(targetNode, nestedSource, prior);
}
}
private ShowlNodeShape findNestedSource(ShowlNodeShape sourceNode, ShowlClass targetClass) {
NestedShapeSelector selector = new NestedShapeSelector(targetClass);
selector.scan(sourceNode.getProperties());
scanDerivedProperties(selector, sourceNode);
selector.scan(sourceNode.getInwardProperties());
return selector.getSelected();
}
private void scanDerivedProperties(NestedShapeSelector selector, ShowlNodeShape sourceNode) {
for (List list : sourceNode.getDerivedProperties()) {
selector.scan(list);
}
}
private void joinObjectProperties(ShowlNodeShape leftNode, ShowlJoinCondition joinCondition) throws ShowlProcessingException {
joinOutwardObjectPropeties(leftNode, joinCondition);
joinInwardObjectProperties(leftNode, joinCondition);
}
private void joinInwardObjectProperties(ShowlNodeShape leftNode, ShowlJoinCondition joinCondition) {
for (ShowlPropertyShape leftProperty : leftNode.getInwardProperties()) {
ShowlClass childClass = leftProperty.getValueType(showlFactory);
if (!isUndefinedClass(childClass)) {
for (ShowlNodeShape rightNode : childClass.getTargetClassOf()) {
// For now consider only named roots.
// TODO: consider shapes that are not named roots.
if (rightNode.isNamedRoot()) {
ShowlPropertyShape rightJoinProperty = rightNode.findProperty(leftProperty.getPredicate());
if (rightJoinProperty != null) {
doJoinInwardObjectProperty(leftProperty, rightJoinProperty, joinCondition);
}
} else if (rightNode.getAccessor()!=leftProperty) {
if (logger.isTraceEnabled()) {
logger.trace("joinInwardObjectProperties: TODO: For {}, handle rightNode {}", leftProperty.getPath(), rightNode.getPath());
}
}
}
} else if (logger.isTraceEnabled()) {
logger.trace("joinInwardObjectProperties: Class is not defined: {}", leftProperty.getPath());
}
}
}
private void doJoinInwardObjectProperty(ShowlPropertyShape leftProperty, ShowlPropertyShape rightJoinProperty,
ShowlJoinCondition joinCondition) {
ShowlPropertyShape leftJoinProperty = leftProperty.getDeclaringShape().findProperty(Konig.id);
ShowlPropertyShape rightProperty = rightJoinProperty.getDeclaringShape().findProperty(Konig.id);
if (leftJoinProperty == null) {
if (logger.isTraceEnabled()) {
logger.trace("doJoinInwardObjectProperty: aborting join because left node does not have an Id: {}", leftProperty.getPath());
}
return;
}
ShowlJoinCondition newJoin = new ShowlTargetToSourceJoinCondition(leftJoinProperty, rightJoinProperty);
new ShowlMapping(newJoin, leftProperty, rightProperty);
if (logger.isTraceEnabled()) {
logger.trace("doJoinInwardObjectProperty: {} ... {}", leftProperty.getPath(), rightJoinProperty.getPath());
}
doBuildMappings(leftProperty.getValueShape(), rightJoinProperty.getDeclaringShape(), newJoin);
}
private void joinOutwardObjectPropeties(ShowlNodeShape leftNode, ShowlJoinCondition joinCondition) throws ShowlProcessingException {
joinOutwardObjectProperties(leftNode.getProperties(), joinCondition);
joinOutwardDerivedObjectProperties(leftNode.getDerivedProperties(), joinCondition);
}
private void joinOutwardDerivedObjectProperties(Collection collection,
ShowlJoinCondition joinCondition) {
for (List list : collection) {
joinOutwardObjectProperties(list, joinCondition);
}
}
private void joinOutwardObjectProperties(Collection extends ShowlPropertyShape> properties,
ShowlJoinCondition joinCondition) throws ShowlProcessingException {
for (ShowlPropertyShape sourceProperty : properties) {
ShowlNodeShape sourceNode = sourceProperty.getValueShape();
if (sourceNode != null) {
ShowlClass childClass = sourceNode.getOwlClass();
if (!isUndefinedClass(childClass)) {
ShowlPropertyShape sourceNodeId = sourceNode.findProperty(Konig.id);
if (sourceNodeId != null) {
buildJoinConditions(sourceNode, MappingRole.SOURCE, joinCondition, null);
}
}
}
}
}
private void doJoin(ShowlNodeShape targetNode, ShowlNodeShape sourceNode, ShowlJoinCondition joinCondition) {
ShowlPropertyShape sourceId = sourceNode.findProperty(Konig.id);
ShowlPropertyShape targetId = targetNode.findProperty(Konig.id);
if (sourceId==null) {
sourceId = useClassIriTemplate(sourceNode);
if (sourceId == null) {
if (logger.isTraceEnabled()) {
logger.trace("Cannot join {}...{} because left Id is not defined", sourceNode.getPath(), targetNode.getPath());
}
return;
}
}
if (targetId==null) {
targetId = useClassIriTemplate(targetNode);
if (targetId == null) {
if (logger.isTraceEnabled()) {
logger.trace("Cannot join {}...{} because right Id is not defined", sourceNode.getPath(), targetNode.getPath());
}
return;
}
}
if (sourceId.findJoinCondition(targetId) == null) {
ShowlJoinCondition join = new ShowlTargetToSourceJoinCondition(targetId, sourceId);
buildMappings(sourceNode, targetNode, join);
}
}
private ShowlPropertyShape useClassIriTemplate(ShowlNodeShape sourceNode) {
ShowlClass owlClass = sourceNode.getOwlClass();
if (owlClass != null) {
IriTemplate template = owlClass.getIriTemplate();
if (template != null) {
ShowlTemplatePropertyShape p = new ShowlTemplatePropertyShape(sourceNode, null, template);
sourceNode.addDerivedProperty(p);
if (logger.isTraceEnabled()) {
logger.trace("useClassIriTemplate({}) ... {}", sourceNode.getPath(), template.getText());
}
return p;
}
}
return null;
}
private void buildMappings(ShowlNodeShape leftNode, ShowlNodeShape rightNode, ShowlJoinCondition join) {
if (logger.isTraceEnabled()) {
logger.trace("buildMappings({}, {})", leftNode.getPath(), rightNode.getPath());
}
doBuildMappings(leftNode, rightNode, join);
// TODO: re-think the following line.
// Do we really want to do this?
doBuildMappings(rightNode, leftNode, join);
}
private void doBuildMappings(ShowlNodeShape leftNode, ShowlNodeShape rightNode, ShowlJoinCondition join) {
if (leftNode==null || rightNode==null) {
return;
}
NodeMapping key = new NodeMapping(leftNode, rightNode, join);
if (!nodeMappings.contains(key)) {
nodeMappings.add(key);
doBuildMappings(leftNode.getProperties(), rightNode, join);
buildDerivedMappings(leftNode.getDerivedProperties(), rightNode, join);
buildInwardMappings(leftNode.getInwardProperties(), rightNode, join);
}
}
private void buildDerivedMappings(Collection collection,
ShowlNodeShape rightNode, ShowlJoinCondition join) {
for (List list : collection) {
doBuildMappings(list, rightNode, join);
}
}
private void buildInwardMappings(Collection extends ShowlPropertyShape> properties, ShowlNodeShape rightNode,
ShowlJoinCondition join) {
for (ShowlPropertyShape leftProperty : properties) {
ShowlPropertyShape rightProperty = rightNode.getInwardProperty(leftProperty.getPredicate());
if (rightProperty==null) {
for (ShowlProperty inverse : leftProperty.getProperty().getInverses()) {
rightProperty = rightNode.findProperty(inverse.getPredicate());
if (rightProperty != null) {
break;
}
}
}
if (rightProperty == null) {
if (logger.isTraceEnabled()) {
logger.trace("buildInwardMapping - Failed to find mapping for {} in {}",
leftProperty.getPath(), rightNode.getPath());
}
continue;
}
ShowlPropertyShape directLeft = directProperty(leftProperty);
ShowlPropertyShape directRight = directProperty(rightProperty);
if (directLeft!=null && directRight!=null && directLeft.getMapping(join)==null) {
produceMapping(join, directLeft, directRight);
}
ShowlNodeShape leftChild = leftProperty.getValueShape();
if (leftChild != null) {
ShowlNodeShape rightChild = rightProperty.getValueShape();
if (rightChild != null) {
buildMappings(leftChild, rightChild, join);
}
}
}
}
private void doBuildMappings(Collection extends ShowlPropertyShape> properties, ShowlNodeShape rightNode,
ShowlJoinCondition join) {
ShowlPropertyShape rightAccessor = rightNode.getAccessor();
for (ShowlPropertyShape leftProperty : properties) {
ShowlPropertyShape rightProperty = rightNode.findProperty(leftProperty.getPredicate());
if (rightProperty == null) {
if (
rightAccessor instanceof ShowlInwardPropertyShape &&
rightAccessor.getPredicate().equals(leftProperty.getPredicate())
) {
ShowlPropertyShape rightId = produceIdProperty(rightAccessor.getDeclaringShape());
produceMapping(join, leftProperty, rightId);
if (leftProperty.getValueShape() != null) {
doBuildMappings(leftProperty.getValueShape(), rightAccessor.getDeclaringShape(), join);
}
} else {
if (reasoner.isEnumerationClass(rightNode.getOwlClass().getId())) {
ShowlNodeShape leftNode = leftProperty.getDeclaringShape();
EnumMappingAction action = enumMappingActions.get(rightNode.getId());
if (action == null) {
action = new EnumMappingAction(leftNode, showlFactory, reasoner);
enumMappingActions.put(leftNode.getId(), action);
}
} else if (logger.isTraceEnabled()) {
logger.trace("doBuildMappings - mapping not found for {} in {}",
leftProperty.getPath(), rightNode.getPath());
}
continue;
}
}
if (Konig.id.equals(leftProperty.getPredicate()) && rightProperty!=null && Konig.id.equals(rightProperty.getPredicate())) {
produceMapping(join, leftProperty, rightProperty);
continue;
}
// if (Konig.id.equals(leftProperty.getPredicate()) || Konig.id.equals(rightProperty.getPredicate())) {
// // TODO: We ought to find a better way to handle konig:id mappings
//
// // For now, we skip them
// if (logger.isTraceEnabled()) {
// logger.trace("doBuildMappings - Skipping mapping: {}...{}",
// leftProperty.getPath(), rightProperty.getPath());
// }
// continue;
// }
leftProperty = propertyToMap(leftProperty);
rightProperty = propertyToMap(rightProperty);
if (leftProperty == null || rightProperty==null) {
continue;
}
produceMapping(join, leftProperty, rightProperty);
ShowlNodeShape leftChild = leftProperty.getValueShape();
if (leftChild == null) {
ShowlPropertyShape peer = leftProperty.getPeer();
if (peer != null) {
leftChild = peer.getValueShape();
}
}
if (leftChild != null) {
ShowlNodeShape rightChild = rightProperty.getValueShape();
if (rightChild != null) {
buildMappings(leftChild, rightChild, join);
}
}
}
}
private ShowlPropertyShape produceIdProperty(ShowlNodeShape rightNode) {
ShowlPropertyShape id = rightNode.findProperty(Konig.id);
if (id == null) {
id = useClassIriTemplate(rightNode);
}
return id;
}
private ShowlPropertyShape propertyToMap(ShowlPropertyShape p) {
ShowlPropertyShape result = directProperty(p);
if (result != null) {
return result;
}
result = derivedByFormula(p);
if (result != null) {
if (logger.isTraceEnabled()) {
logger.trace("propertyToMap({}) => {}", p.getPath(), result.getPath());
}
return result;
}
return p;
}
private ShowlDerivedPropertyShape derivedByFormula(ShowlPropertyShape p) {
if (p instanceof ShowlDerivedPropertyShape) {
ShowlDerivedPropertyShape derived = (ShowlDerivedPropertyShape) p;
if (derived.getPropertyConstraint()!=null && derived.getPropertyConstraint().getFormula()!=null) {
return derived;
}
}
return null;
}
private void produceMapping(ShowlJoinCondition join, ShowlPropertyShape left,
ShowlPropertyShape right) {
if (join.isJoinProperty(left) && join.isJoinProperty(right)) {
new ShowlJoinMapping(join);
} else {
new ShowlMapping(join, left, right);
}
}
private ShowlPropertyShape directProperty(ShowlPropertyShape p) {
return
p==null ? null :
p.isDirect() ? p : p.getPeer();
}
private boolean isUndefinedClass(ShowlClass owlClass) {
return ShowlUtil.isUndefinedClass(owlClass);
}
private void buildMapping(
ShowlJoinCondition join,
ShowlNodeShape leftNode,
ShowlPropertyShape leftProperty,
ShowlNodeShape rightNode) {
if (logger.isTraceEnabled()) {
logger.trace("buildMapping: {}...{}", leftProperty.getPath(), rightNode.getPath());
}
if (leftProperty.getMapping(join)==null) {
ShowlPropertyShape rightProperty = rightNode.findProperty(leftProperty.getPredicate());
if (rightProperty == null) {
Set set = leftProperty.getProperty().getConnectedProperties();
for (ShowlProperty q : set) {
if (q == leftProperty.getProperty()) {
continue;
}
rightProperty = rightNode.findProperty(q.getPredicate());
if (rightProperty != null) {
break;
}
}
}
if (rightProperty != null) {
if (rightProperty.isLeaf() && !rightProperty.isDirect()) {
rightProperty = rightProperty.getPeer();
if (rightProperty == null) {
return;
}
}
ShowlMapping mapping = new ShowlMapping(join, leftProperty, rightProperty);
leftProperty.addMapping(mapping);
rightProperty.addMapping(mapping);
buildNestedMappings(join, leftProperty, rightProperty);
}
}
}
private void buildNestedMappings(ShowlJoinCondition join, ShowlPropertyShape leftProperty,
ShowlPropertyShape rightProperty) {
ShowlNodeShape leftNode = leftProperty.getValueShape();
ShowlNodeShape rightNode = rightProperty.getValueShape();
if (leftNode!=null && rightNode!=null) {
for (ShowlPropertyShape p : leftNode.getProperties()) {
buildMapping(join, leftNode, p, rightNode);
}
}
}
public void inferTargetClasses() {
for (ShowlNodeShape gns : classlessShapes) {
inferTargetClass(gns);
}
classlessShapes = null;
}
private void inferTargetClass(ShowlNodeShape node) {
if (!ShowlUtil.isUndefinedClass(node.getOwlClass())) {
return;
}
DomainReasoner domainReasoner = new DomainReasoner(reasoner);
// TODO: handle derived properties separately.
Set allProperties = node.allOutwardProperties();
for (ShowlPropertyShape p : allProperties) {
if (p.isNestedAccordingToFormula()) {
continue;
}
ShowlProperty property = p.getProperty();
if (property != null) {
if (property.getDomain() != null) {
domainReasoner.require(property.getDomain().getId());
} else {
domainReasoner.domainIncludes(property.domainIncludes(showlFactory));
}
}
}
Set candidates = domainReasoner.getRequiredClasses();
if (candidates.isEmpty()) {
candidates = inferTargetClassFromFormula(node, domainReasoner);
}
if (candidates.size()==1) {
URI owlClass = candidates.iterator().next();
replaceOwlClass(node, owlClass);
if (logger.isTraceEnabled()) {
logger.trace("inferTargetClass: Set {} as target class of {}", owlClass.getLocalName(), node.getPath());
}
} else {
if (logger.isWarnEnabled()) {
if (candidates.isEmpty()) {
candidates = domainReasoner.getAllClasses();
}
if (candidates.isEmpty()) {
logger.warn("No candidates found for target class of " + node.getPath());
} else {
StringBuilder builder = new StringBuilder();
builder.append("Target class at " + node.getPath() + " is ambiguous. Candidates include\n");
for (URI c : candidates) {
builder.append(" ");
builder.append(c.getLocalName());
builder.append('\n');
}
logger.warn(builder.toString());
}
}
}
}
/**
* Add OWL classes from the domain of a given Property to the set of candidates.
* @param candidates The set of candidates to be augmented.
* @param property
* @return true if there is no conflict between the existing candidates and the given property
*/
private boolean updateCandidates(Set candidates, ShowlProperty property) {
Set domainIncludes = property.domainIncludes(showlFactory);
// Remove owl:Thing and konig:Undefined from consideration
Iterator sequence = domainIncludes.iterator();
while (sequence.hasNext()) {
URI domain = sequence.next();
if (OWL.THING.equals(domain) || Konig.Undefined.equals(domain)) {
sequence.remove();
}
}
if (domainIncludes.isEmpty()) {
return true;
}
if (candidates.isEmpty()) {
candidates.addAll(domainIncludes);
return true;
}
boolean compatible = true;
domainLoop : for (URI domain : domainIncludes) {
sequence = candidates.iterator();
while (sequence.hasNext()) {
URI candidate = sequence.next();
if (domain.equals(candidate)) {
continue domainLoop;
}
if (reasoner.isSubClassOf(domain, candidate)) {
// replace candidate with the more narrow domain
sequence.remove();
candidates.add(domain);
continue domainLoop;
}
if (reasoner.isSubClassOf(candidate, domain)) {
// The domain element is consistent with the existing
// candidates since there is a candidate that is a
// subclass of the domain.
// Keep the more narrow candidate.
continue domainLoop;
}
}
}
return compatible;
}
private Set inferTargetClassFromFormula(ShowlNodeShape node, DomainReasoner domainReasoner) {
ShowlPropertyShape accessor = node.getAccessor();
if (accessor != null) {
ShowlClass owlClass = accessor.getProperty().getRange();
if (owlClass != null) {
return setOf(owlClass.getId());
}
ShowlPropertyShape peer = accessor.getPeer();
if (peer != null) {
owlClass = peer.getProperty().getRange();
if (owlClass != null) {
return setOf(owlClass.getId());
}
}
}
boolean updated = false;
for (ShowlPropertyShape p : node.getProperties()) {
PropertyConstraint constraint = p.getPropertyConstraint();
if (constraint != null) {
QuantifiedExpression formula = constraint.getFormula();
if (formula != null) {
PrimaryExpression primary = formula.asPrimaryExpression();
if (primary instanceof PathExpression) {
PathExpression path = (PathExpression) primary;
List stepList = path.getStepList();
if (stepList.size() == 1) {
PathStep step = stepList.get(0);
if (step instanceof DirectionStep) {
DirectionStep dirStep = (DirectionStep) step;
if (dirStep.getDirection() == Direction.OUT) {
URI predicate = dirStep.getTerm().getIri();
ShowlProperty property = getProperty(predicate);
if (property != null) {
Set domainIncludes = property.domainIncludes(showlFactory);
domainReasoner.domainIncludes(domainIncludes);
updated = true;
}
}
}
}
}
}
}
}
return updated ? domainReasoner.getRequiredClasses() : Collections.emptySet();
}
@SuppressWarnings("unchecked")
private Set setOf(T... elements) {
Set result = new HashSet();
for (T e : elements) {
result.add(e);
}
return result;
}
private void replaceOwlClass(ShowlNodeShape node, URI owlClassId) {
ShowlClass newClass = produceOwlClass(owlClassId);
node.setOwlClass(newClass);
if (logger.isDebugEnabled()) {
logger.debug("Set OWL Class of " + node.getPath() + " as " + "<" + owlClassId.stringValue() + ">");
}
}
protected void loadShapes() {
classlessShapes = new ArrayList<>();
Set rootShapes = selectShapes();
for (Shape shape : rootShapes) {
createNodeShape(null, shape);
}
}
protected Set selectShapes() {
Set result = new LinkedHashSet<>();
Map hasReference = new LinkedHashMap<>();
List shapeList = shapeManager.listShapes();
for (Shape shape : shapeList) {
putReferences(shape.getProperty(), hasReference);
}
for (Shape shape : shapeList) {
if (!shape.getShapeDataSource().isEmpty() || hasReference.get(shape)==null) {
result.add(shape);
}
}
return result;
}
protected void putReferences(List property, Map hasReference) {
for (PropertyConstraint p : property) {
Shape shape = p.getShape();
if (shape != null) {
if (hasReference.put(shape, Boolean.TRUE)==null) {
putReferences(shape.getProperty(), hasReference);
}
}
}
}
private ShowlNodeShape createShowlNodeShape(ShowlPropertyShape accessor, Shape shape, ShowlClass owlClass) {
ShowlNodeShape result = new ShowlNodeShape(this, accessor, shape, owlClass);
owlClass.addTargetClassOf(result);
if (Konig.Undefined.equals(owlClass.getId())) {
if (classlessShapes != null) {
classlessShapes.add(result);
}
}
ShowlNodeShapeSet set = nodeShapes.get(shape.getId());
if (set == null) {
set = new ShowlNodeShapeSet();
nodeShapes.put(shape.getId(), set);
}
set.add(result);
return result;
}
private ShowlNodeShape createNodeShape(ShowlPropertyShape accessor, Shape shape) {
ShowlClass owlClass = targetOwlClass(accessor, shape);
ShowlNodeShape result = createShowlNodeShape(accessor, shape, owlClass);
addProperties(result);
return result;
}
private ShowlClass targetOwlClass(ShowlPropertyShape accessor, Shape shape) {
if (accessor != null) {
PropertyConstraint p = accessor.getPropertyConstraint();
if (p != null) {
if (p.getValueClass() instanceof URI) {
return produceOwlClass((URI)p.getValueClass());
}
}
}
URI classId = shape.getTargetClass();
if (classId ==null) {
classId = Konig.Undefined;
}
return produceOwlClass(classId);
}
void addProperties(ShowlNodeShape declaringShape) {
if (logger.isTraceEnabled()) {
logger.trace("addProperties({})", declaringShape.getPath());
}
addIdProperty(declaringShape);
for (PropertyConstraint p : declaringShape.getShape().getVariable()) {
URI predicate = p.getPredicate();
ShowlProperty property = produceShowlProperty(predicate);
property.setDomain(declaringShape.getOwlClass());
property.setRange(produceOwlClass(RdfUtil.uri(p.getValueClass())));
ShowlOutwardPropertyShape out = new ShowlVariablePropertyShape(declaringShape, property);
property.addPropertyShape(out);
declaringShape.addDerivedProperty(out);
if (logger.isTraceEnabled()) {
logger.trace("addProperties: add variable ?{} to {}", predicate.getLocalName(), declaringShape.getPath());
}
}
for (PropertyConstraint p : declaringShape.getShape().getProperty()) {
URI predicate = p.getPredicate();
if (predicate != null) {
ShowlProperty property = produceShowlProperty(predicate);
ShowlDirectPropertyShape q = createDirectPropertyShape(declaringShape, property, p);
declaringShape.addProperty(q);
processFormula(q);
Shape childShape = p.getShape();
if (childShape != null) {
if (declaringShape.hasAncestor(childShape.getId())) {
error("Cyclic shape detected at: " + q.getPath());
}
createNodeShape(q, childShape);
}
}
}
for (PropertyConstraint p : declaringShape.getShape().getDerivedProperty()) {
URI predicate = p.getPredicate();
if (predicate != null) {
ShowlProperty property = produceShowlProperty(predicate);
ShowlFormulaPropertyShape q = createFormulaPropertyShape(declaringShape, property, p);
declaringShape.addDerivedProperty(q);
Shape childShape = p.getShape();
if (childShape != null) {
if (declaringShape.hasAncestor(childShape.getId())) {
error("Cyclic shape detected at: " + q.getPath());
}
// TODO: create node shape for childShape.
error("child shape of derived property not supported yet for " + q.getPath());
}
}
}
}
private ShowlFormulaPropertyShape createFormulaPropertyShape(ShowlNodeShape declaringShape, ShowlProperty property,
PropertyConstraint constraint) {
ShowlFormulaPropertyShape p = new ShowlFormulaPropertyShape(declaringShape, property, constraint);
if (logger.isTraceEnabled()) {
logger.trace("createDerivedPropertyShape: {}", p.getPath());
}
return p;
}
void addIdProperty(ShowlNodeShape declaringShape) {
if (declaringShape.getShape().getIriTemplate() != null) {
ShowlProperty property = produceShowlProperty(Konig.id);
ShowlOutwardPropertyShape out = new ShowlOutwardPropertyShape(declaringShape, property);
out.setFormula(ShowlFunctionExpression.fromIriTemplate(
showlFactory, showlFactory, out, declaringShape.getShape().getIriTemplate()));
declaringShape.addDerivedProperty(out);
} else if (declaringShape.getShape().getNodeKind() == NodeKind.IRI) {
ShowlProperty property = produceShowlProperty(Konig.id);
ShowlDirectPropertyShape p = createDirectPropertyShape(declaringShape, property, null);
declaringShape.addProperty(p);
}
}
private ShowlDirectPropertyShape createDirectPropertyShape(ShowlNodeShape declaringShape, ShowlProperty property,
PropertyConstraint constraint) {
ShowlDirectPropertyShape p = new ShowlDirectPropertyShape(declaringShape, property, constraint);
property.addPropertyShape(p);
if (logger.isTraceEnabled()) {
logger.trace("createDirectPropertyShape: created {}", p.getPath());
}
return p;
}
private void error(String text) {
logger.error(text);
}
protected ShowlProperty produceShowlProperty(URI predicate) {
ShowlProperty property = properties.get(predicate);
if (property == null) {
property = new ShowlProperty(predicate);
properties.put(predicate, property);
URI domain = RdfUtil.uri(reasoner.getDomain(predicate));
URI range = RdfUtil.uri(reasoner.getRange(predicate));
if (domain != null) {
property.setDomain(produceOwlClass(domain));
}
if (range != null) {
property.setRange(produceOwlClass(range));
}
}
return property;
}
private void processFormula(ShowlPropertyShape ps) {
PropertyConstraint p = ps.getPropertyConstraint();
QuantifiedExpression e = (p==null) ? null : p.getFormula();
if (e != null) {
if (logger.isTraceEnabled()) {
logger.trace("processFormula({})", ps.getPath());
}
setShowlExpression(ps, e);
// TODO: We should be able to eliminate this dispatch...
e.dispatch(new PathVisitor(ps));
}
}
private void setShowlExpression(ShowlPropertyShape ps, QuantifiedExpression e) {
ShowlExpressionBuilder builder = new ShowlExpressionBuilder(showlFactory, showlFactory);
ShowlExpression ex = builder.expression(ps, e);
ps.setFormula(ex);
}
class HasPathVisitor extends PathVisitor {
public HasPathVisitor(ShowlPropertyShape propertyShape) {
super(propertyShape);
}
@Override
protected ShowlNodeShape targetShape() {
return propertyShape.getValueShape();
}
@Override
protected void setPeer() {
}
}
class PathVisitor implements FormulaVisitor {
protected ShowlPropertyShape propertyShape;
private ShowlPropertyShape prior;
private int depth=0;
public PathVisitor(ShowlPropertyShape propertyShape) {
this.propertyShape = propertyShape;
}
public ShowlPropertyShape getLast() {
return prior;
}
@Override
public void enter(Formula formula) {
if (formula instanceof HasPathStep) {
enterHasPathStep();
}
if (formula instanceof PathExpression) {
PathExpression path = (PathExpression) formula;
ShowlNodeShape declaringShape = targetShape();
if (logger.isTraceEnabled()) {
logger.debug("PathVisitor.enter(declaringShape: {}, formula: {}", declaringShape.getPath(),
((PathExpression) formula).simpleText());
}
String shapeIdValue = declaringShape.getShape().getId().stringValue();
prior = null;
List stepList = path.getStepList();
for (int i = 0; i < stepList.size(); i++) {
PathStep step = stepList.get(i);
if (step instanceof DirectionStep) {
DirectionStep dirStep = (DirectionStep) step;
URI predicate = dirStep.getTerm().getIri();
ShowlProperty property = produceShowlProperty(predicate);
ShowlNodeShape parentShape = null;
ShowlPropertyShape thisStep = null;
shapeIdValue += dirStep.getDirection().getSymbol() + predicate.getLocalName();
switch (dirStep.getDirection()) {
case OUT: {
if (i == 0) {
if (dirStep.getTerm() instanceof VariableTerm) {
parentShape = varRoot(dirStep.getTerm());
} else {
parentShape = declaringShape;
}
} else {
// Get OWL Class for parent shape, i.e. the
// property domain.
ShowlClass owlClass = property.inferDomain(showlFactory);
DirectionStep prevStep = path.directionStepBefore(i);
ShowlClass prevClass = valueClassOf(prevStep);
owlClass = theMostSpecificClass(owlClass, prevClass);
parentShape = createNodeShape(prior, shapeIdValue, owlClass);
}
ShowlPropertyShape p = outwardProperty(parentShape, property, i);
thisStep = p;
break;
}
case IN: {
if (i == 0) {
parentShape = declaringShape;
} else {
// Get OWL Class for parent shape, i.e. the
// property range.
ShowlClass owlClass = property.inferRange(showlFactory);
DirectionStep prevStep = path.directionStepBefore(i);
ShowlClass prevClass = valueClassOf(prevStep);
owlClass = theMostSpecificClass(owlClass, prevClass);
parentShape = createNodeShape(prior, shapeIdValue, owlClass);
}
ShowlInwardPropertyShape p = inwardProperty(parentShape, property);
thisStep = p;
}
}
prior = thisStep;
} else {
if (prior == null) {
error("Top-level filter not supported");
}
ShowlNodeShape valueShape = prior.getValueShape();
if (valueShape == null) {
ShowlProperty property = produceShowlProperty(prior.getPredicate());
ShowlClass owlClass = property.inferRange(showlFactory);
valueShape = createNodeShape(prior, shapeIdValue, owlClass);
prior.setValueShape(valueShape);
}
buildHasStep(prior, (HasPathStep) step);
}
}
setPeer();
}
}
private ShowlNodeShape varRoot(PathTerm term) {
URI predicate = term.getIri();
for (ShowlPropertyShape p=propertyShape; p!=null;) {
ShowlNodeShape node = p.getDeclaringShape();
Shape shape = node.getShape();
if (shape == null) {
fail("Declaring Shape is null at {0}", p.getPath());
}
if (shape.getVariableById(predicate) != null) {
return node;
}
p = node.getAccessor();
}
fail("Root node for ?{0} not found in formula of {1}", predicate.getLocalName(), propertyShape.getPath());
return null;
}
private void enterHasPathStep() {
depth++;
}
protected void setPeer() {
if (depth==0 && prior != null) {
QuantifiedExpression formula = propertyShape.getPropertyConstraint().getFormula();
if (formula.asPrimaryExpression() instanceof PathExpression) {
propertyShape.addPeer(prior);
addExpression(propertyShape, prior);
addExpression(prior, propertyShape);
}
}
}
protected ShowlNodeShape targetShape() {
return propertyShape.getDeclaringShape();
}
private void buildHasStep(ShowlPropertyShape p, HasPathStep step) {
if (logger.isDebugEnabled()) {
logger.debug("buildHasStep(propertyShape: {}, step: {})", p.getPath(), step.toSimpleString());
}
for (PredicateObjectList pol : step.getConstraints()) {
PathExpression path = pol.getPath();
HasPathVisitor predicateVisitor = new HasPathVisitor(p);
path.dispatch(predicateVisitor);
ShowlPropertyShape last = predicateVisitor.getLast();
for (Expression e : pol.getObjectList().getExpressions()) {
PrimaryExpression primary = e.asPrimaryExpression();
if (primary == null) {
error("Expression not supported");
}
if (primary instanceof IriValue) {
IriValue value = (IriValue) primary;
URI iri = value.getIri();
last.addHasValueDeprecated(iri);
} else if (primary instanceof LiteralFormula) {
LiteralFormula formula = (LiteralFormula) primary;
last.addHasValueDeprecated(formula.getLiteral());
}
}
}
}
private ShowlInwardPropertyShape inwardProperty(ShowlNodeShape parentShape, ShowlProperty property) {
ShowlInwardPropertyShape prior = parentShape.getInwardProperty(property.getPredicate());
if (prior != null) {
return prior;
}
ShowlInwardPropertyShape p = new ShowlInwardPropertyShape(parentShape, property);
parentShape.addInwardProperty(p);
return p;
}
private ShowlPropertyShape outwardProperty(ShowlNodeShape parentShape, ShowlProperty property, int i) {
ShowlPropertyShape prior = parentShape.findProperty(property.getPredicate());
if (prior != null) {
return prior;
}
PropertyConstraint c = null;
if (i == 0) {
c = new PropertyConstraint(property.getPredicate());
c.setNodeKind(propertyShape.getNodeKind());
}
ShowlOutwardPropertyShape p = new ShowlOutwardPropertyShape(parentShape, property, c);
property.addPropertyShape(p);
parentShape.addDerivedProperty(p);
if (logger.isTraceEnabled()) {
logger.trace("outwardProperty: created {}", p.getPath());
}
return p;
}
private ShowlNodeShape createNodeShape(ShowlPropertyShape accessor, String shapeIdValue,
ShowlClass owlClass) {
ShowlNodeShape value = accessor.getValueShape();
if (value != null) {
return value;
}
URI shapeId = new URIImpl(shapeIdValue);
Shape shape = new Shape(shapeId);
NodeKind kind = accessor.getNodeKind();
if (kind == null) {
ShowlNodeShape nestedShape = accessor.getValueShape();
if (nestedShape != null) {
kind = nestedShape.getNodeKind();
}
}
shape.setNodeKind(kind);
ShowlNodeShape node = createShowlNodeShape(accessor, shape, owlClass);
if (kind == NodeKind.IRI) {
ShowlProperty konigId = produceShowlProperty(Konig.id);
ShowlIdRefPropertyShape p = new ShowlIdRefPropertyShape(node, konigId, propertyShape);
node.addDerivedProperty(p);
}
return node;
}
/**
* Compute the OWL Class for the value obtained by traversing a given step.
*/
private ShowlClass valueClassOf(DirectionStep step) {
if (step != null) {
ShowlProperty property = produceShowlProperty(step.getTerm().getIri());
switch (step.getDirection()) {
case OUT: return property.inferRange(showlFactory);
case IN: return property.inferDomain(showlFactory);
}
}
return produceOwlClass(Konig.Undefined);
}
@Override
public void exit(Formula formula) {
if (formula instanceof HasPathStep) {
depth--;
}
}
}
private ShowlClass theMostSpecificClass(ShowlClass a, ShowlClass b) {
ShowlClass result =
a==null ? b :
b==null ? a :
reasoner.isSubClassOf(a.getId(), b.getId()) ? a :
b;
return result==null ? produceOwlClass(Konig.Undefined) : result;
}
ShowlClass produceOwlClass(URI owlClass) {
if (owlClass == null) {
owlClass = Konig.Undefined;
}
ShowlClass result = owlClasses.get(owlClass);
if (result == null) {
result = new ShowlClass(getReasoner(), owlClass);
owlClasses.put(owlClass, result);
}
return result;
}
private static class NodeMapping {
private ShowlNodeShape leftNode;
private ShowlNodeShape rightNode;
private ShowlJoinCondition join;
public NodeMapping(ShowlNodeShape leftNode, ShowlNodeShape rightNode, ShowlJoinCondition join) {
this.leftNode = leftNode;
this.rightNode = rightNode;
this.join = join;
}
public boolean equals(Object other) {
if (other instanceof NodeMapping) {
final NodeMapping b = (NodeMapping) other;
return b.leftNode==this.leftNode && b.rightNode==this.rightNode && b.join==this.join;
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(leftNode, rightNode, join);
}
}
private static enum MappingRole {
SOURCE,
TARGET
}
private class DomainReasoner {
private OwlReasoner reasoner;
private Set candidates = new HashSet<>();
public DomainReasoner(OwlReasoner reasoner) {
this.reasoner = reasoner;
}
public void require(URI owlClass) {
candidates.add(new ModalClass(owlClass, true));
}
public void domainIncludes(Set domainIncludes) {
boolean required = domainIncludes.size()==1;
for (URI domain : domainIncludes) {
URI matched = null;
for (ModalClass modal : candidates) {
URI candidate = modal.getOwlClass();
if (reasoner.isSubClassOf(domain, candidate)) {
// The domain is more narrow than the candidate
// so replace the candidate with the domain.
modal.update(domain, required);
matched = domain;
} else if (reasoner.isSubClassOf(candidate, domain)) {
// The existing candidate is more narrow than the domain
// so keep the existing candidate
modal.update(candidate, required);
matched = candidate;
}
}
if (matched==null) {
candidates.add(new ModalClass(domain, required));
}
}
}
public Set getRequiredClasses() {
Set result = new HashSet<>();
for (ModalClass modal : candidates) {
if (modal.isRequired()) {
result.add(modal.getOwlClass());
}
}
return result;
}
public Set getAllClasses() {
Set result = new HashSet<>();
for (ModalClass modal : candidates) {
result.add(modal.getOwlClass());
}
return result;
}
}
/**
* A structure that decorates the URI for an OWL class with
* a flag that specifies whether or not the class is required.
*
*/
private class ModalClass {
private boolean required;
private URI owlClass;
public ModalClass(URI owlClass, boolean required) {
this.required = required;
this.owlClass = owlClass;
}
public boolean isRequired() {
return required;
}
public URI getOwlClass() {
return owlClass;
}
public void update(URI owlClass, boolean required) {
this.owlClass = owlClass;
if (required) {
this.required = true;
}
}
}
private static class NestedShapeSelector {
private ShowlNodeShape selected;
private ShowlClass owlClass;
private boolean failed=false;
public NestedShapeSelector(ShowlClass owlClass) {
this.owlClass = owlClass;
}
void scan(Collection extends ShowlPropertyShape> list) {
if (!failed) {
for (ShowlPropertyShape p : list) {
ShowlNodeShape nested = p.getValueShape();
if (nested != null && nested.getOwlClass().isSubClassOf(owlClass)) {
if (selected == null) {
selected = nested;
} else {
if (logger.isTraceEnabled() && failed == false) {
logger.trace(
"Nested shape selection is ambiguous. Options include {} and {}",
selected.getPath(), nested.getPath());
}
failed = true;
return;
}
}
}
}
}
public ShowlNodeShape getSelected() {
return failed ? null : selected;
}
}
@Override
public Collection listClasses() {
return owlClasses.values();
}
@Override
public ShowlClass findClassById(URI classId) {
return owlClasses.get(classId);
}
public class Factory implements ShowlService {
@Override
public ShowlNodeShape logicalNodeShape(URI owlClass) throws ShowlProcessingException {
NamespaceManager nsManager = reasoner.getGraph().getNamespaceManager();
Namespace ns = nsManager.findByName(owlClass.getNamespace());
if (ns == null) {
throw new ShowlProcessingException("Prefix not found for namespace <" + owlClass.getNamespace() + ">");
}
StringBuilder builder = new StringBuilder();
builder.append("urn:konig:logicalShape:");
builder.append(ns.getPrefix());
builder.append(':');
builder.append(owlClass.getLocalName());
URI shapeId = new URIImpl(builder.toString());
ShowlNodeShapeSet set = nodeShapes.get(shapeId);
if (set == null) {
set = new ShowlNodeShapeSet();
nodeShapes.put(shapeId, set);
Shape shape = new Shape(shapeId);
ShowlClass showlClass = produceOwlClass(owlClass);
shape.addShapeDataSource(staticDataSource());
ShowlNodeShape node = new ShowlNodeShape(ShowlManager.this, null, shape, showlClass);
showlClass.addTargetClassOf(node);
set.add(node);
// For now, we assume that every logical shape describes a named individual.
// We may need to back off that assumption in the future.
shape.setNodeKind(NodeKind.IRI);
shape.setTargetClass(owlClass);
addIdProperty(node);
node.setShapeDataSource(new ShowlDataSource(node, staticDataSource()));
return node;
}
return set.top();
}
@Override
public ShowlNodeShape createNodeShape(Shape shape) throws ShowlProcessingException {
URI owlClass = shape.getTargetClass();
if (owlClass == null) {
throw new ShowlProcessingException("sh:targetClass must be defined for shape " +
RdfUtil.compactName(getReasoner().getGraph().getNamespaceManager(), shape.getId()));
}
ShowlClass showlClass = targetOwlClass(null, shape);
ShowlNodeShape nodeShape = new ShowlNodeShape(ShowlManager.this, null, shape, showlClass);
showlClass.addTargetClassOf(nodeShape);
addProperties(nodeShape);
if (logger.isTraceEnabled()) {
logger.trace("Factory.createNodeShape({})", RdfUtil.localName(shape.getId()) );
}
return nodeShape;
}
@Override
public ShowlNodeShape createNodeShape(Shape shape, DataSource ds) throws ShowlProcessingException {
ShowlNodeShape node = createNodeShape(shape);
if (ds != null) {
node.setShapeDataSource(new ShowlDataSource(node, ds));
}
return node;
}
@Override
public ShowlProperty produceProperty(URI predicate) throws ShowlProcessingException {
return produceShowlProperty(predicate);
}
@Override
public ShowlClass inferDomain(ShowlProperty p) {
return p.inferDomain(showlFactory);
}
@Override
public ShowlClass inferRange(ShowlProperty p) {
return p.inferRange(showlFactory);
}
@Override
public ShowlClass mostSpecificClass(ShowlClass a, ShowlClass b) {
return theMostSpecificClass(a, b);
}
@Override
public ShowlNodeShape createShowlNodeShape(ShowlPropertyShape accessor, Shape shape, ShowlClass owlClass) {
ShowlNodeShape result = new ShowlNodeShape(ShowlManager.this, accessor, shape, owlClass);
owlClass.addTargetClassOf(result);
if (Konig.Undefined.equals(owlClass.getId())) {
if (classlessShapes != null) {
classlessShapes.add(result);
}
}
ShowlNodeShapeSet set = nodeShapes.get(shape.getId());
if (set == null) {
set = new ShowlNodeShapeSet();
nodeShapes.put(shape.getId(), set);
}
set.add(result);
return result;
}
@Override
public Set selectCandidateSources(ShowlNodeShape targetShape) {
return ShowlManager.this.selectCandidateSources(targetShape);
}
@Override
public Graph getGraph() {
return reasoner.getGraph();
}
@Override
public ShapeManager getShapeManager() {
return shapeManager;
}
@Override
public ShowlClass produceShowlClass(URI classId) {
return ShowlManager.this.produceOwlClass(classId);
}
@Override
public OwlReasoner getOwlReasoner() {
return reasoner;
}
@Override
public Shape enumNodeShape(ShowlClass enumClass) throws ShowlProcessingException {
throw new ShowlProcessingException("enumNodeShape method not supported");
}
}
private void addExpression(ShowlPropertyShape target, ShowlPropertyShape source) {
if (source instanceof ShowlDirectPropertyShape) {
target.addExpression(new ShowlDirectPropertyExpression((ShowlDirectPropertyShape)source));
} else {
ShowlDerivedPropertyShape derived = (ShowlDerivedPropertyShape) source;
addClassIriTemplateFormula(target, derived);
if (derived.getFormula() != null) {
target.addExpression(derived.getFormula());
} else {
target.addExpression(new ShowlDerivedPropertyExpression((ShowlDerivedPropertyShape)source));
}
}
ShowlNodeShape targetNode = target.getValueShape();
if (targetNode != null && !reasoner.isEnumerationClass(targetNode.getOwlClass().getId())) {
ShowlNodeShape sourceNode = source.getValueShape();
if (sourceNode == null) {
ShowlPropertyShape synonym = source.getSynonym();
if (synonym != null) {
sourceNode = synonym.getValueShape();
}
}
if (sourceNode!=null) {
List list = new ArrayList<>();
list.add(sourceNode);
buildTransforms(targetNode, list);
}
}
}
/**
* Use the IRI Template from the OWL Class to inject a formula for the target IRI.
*/
private void addClassIriTemplateFormula(ShowlPropertyShape target, ShowlDerivedPropertyShape derived) {
if (derived.getFormula()==null && derived.getValueShape()!=null) {
ShowlClass owlClass = target.getValueType(showlFactory);
Vertex v = reasoner.getGraph().getVertex(owlClass.getId());
if (v != null) {
Value templateValue = v.getValue(Konig.iriTemplate);
if (templateValue != null) {
ShowlNodeShape node = derived.getValueShape();
IriTemplate classTemplate = new IriTemplate(templateValue.stringValue());
Context classContext = classTemplate.getContext();
classContext.compile();
IriTemplate template = new IriTemplate();
BasicContext context = new BasicContext("");
template.setContext(context);
for (ValueFormat.Element e : classTemplate.toList()) {
switch (e.getType()) {
case TEXT :
template.addText(e.getText());
break;
case VARIABLE :
URI termId = new URIImpl(classContext.expandIRI(e.getText()));
ShowlPropertyShape p = node.findOut(termId);
if (p != null) {
if (p instanceof ShowlDerivedPropertyShape) {
p = p.getSynonym();
}
if (p instanceof ShowlDirectPropertyShape) {
URI predicate = p.getPredicate();
context.addTerm(predicate.getLocalName(), predicate.stringValue());
template.addVariable(predicate.getLocalName());
} else {
// abort
return;
}
}
break;
}
}
ShowlExpression e = ShowlFunctionExpression.fromIriTemplate(
showlFactory, showlFactory, derived, template);
derived.setFormula(e);
}
}
}
}
}