All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.konig.core.showl.ShowlManager Maven / Gradle / Ivy

There is a newer version: 2.11.0
Show newest version
package io.konig.core.showl;

/*
 * #%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.impl.URIImpl;
import org.openrdf.model.vocabulary.OWL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.konig.core.NamespaceManager;
import io.konig.core.OwlReasoner;
import io.konig.core.impl.RdfUtil;
import io.konig.core.util.IriTemplate;
import io.konig.core.vocab.Konig;
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.PredicateObjectList;
import io.konig.formula.PrimaryExpression;
import io.konig.formula.QuantifiedExpression;
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 ShowlSourceNodeSelector sourceNodeSelector;
	private Map enumMappingActions;
	private ShowlFactory showlFactory;
	
	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 ShapeManager getShapeManager() {
		return shapeManager;
	}
	
	public void load() throws ShowlProcessingException {

		clear();
		loadShapes();
		inferTargetClasses();
		inferInverses();
		buildJoinConditions();
	}
	
	public Set listNodeShapeIds() {
		return nodeShapes.keySet();
	}
	
	public ShowlNodeShapeSet getNodeShape(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;
		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 buildJoinConditions(ShowlNodeShape nodeA, MappingRole role, ShowlJoinCondition joinCondition, ShowlNodeShapeConsumer consumer) throws ShowlProcessingException {
		if (nodeA.getAccessor()==null && nodeA.hasDataSource() && !isUndefinedClass(nodeA.getOwlClass())) {
			Set candidates = sourceNodeSelector.selectCandidateSources(nodeA);
			if (candidates.isEmpty() && role==MappingRole.TARGET) {
				nodeA.setUnmapped(true);
			}
			boolean joinObjectProperties = false;
			for (Shape shapeB : candidates) {
				
				if (shapeB == nodeA.getShape()) {
					// Identity mapping
					ShowlNodeShape nodeB = createNodeShape(null, nodeA.getShape());
					ShowlNodeShape targetNode = role==MappingRole.TARGET ? nodeA : nodeB;
					ShowlNodeShape sourceNode = role==MappingRole.TARGET ? nodeB : nodeA;
					createIdentityMapping(sourceNode, targetNode, null);
					continue;
				}
				
				for (ShowlNodeShape nodeB : getNodeShape(shapeB.getId())) {
					
					
					ShowlClass classA = nodeA.getOwlClass();
					ShowlClass classB = nodeB.getOwlClass();

					ShowlNodeShape targetNode = role==MappingRole.TARGET ? nodeA : nodeB;
					ShowlNodeShape sourceNode = role==MappingRole.TARGET ? nodeB : nodeA;
					
					joinObjectProperties = true;
					if (classA.isSubClassOf(classB) || classB.isSubClassOf(classA)) {
						
						doJoin(targetNode, sourceNode, joinCondition);
					} else {
						joinNested(sourceNode, targetNode, joinCondition);
					}
				}
			}
			
			if (joinObjectProperties) {
				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 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(ShowlManager.this);
			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 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 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 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(this));
				}
			}
		}
		
		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(this);
		
		// 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) {
		
		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(this);
										
										domainReasoner.domainIncludes(domainIncludes);
										updated = true;
										
									}
								}
							}
						}
					}
				}
			}
		}
		
		return updated ? domainReasoner.getRequiredClasses() : Collections.emptySet();
		
	}

	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(accessor, shape, owlClass);
		if (Konig.Undefined.equals(owlClass.getId())) {
			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().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);
			ShowlTemplatePropertyShape p = new ShowlTemplatePropertyShape(
					declaringShape, property, declaringShape.getShape().getIriTemplate());
			declaringShape.addDerivedProperty(p);
		} 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);
		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());
			}
			e.dispatch(new PathVisitor(ps));
		}
		
	}

	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 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 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 ShowlFactory {

		@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);
				
				ShowlNodeShape node = new ShowlNodeShape(null, shape, showlClass);
				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);
				addIdProperty(node);
				
				return node;
			}
			
			return set.top();
		}

		
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy