/******************************************************************************* * Copyright 2012 University of Southern California * * 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. * * This code was developed by the Information Integration Group as part * of the Karma project at the Information Sciences Institute of the * University of Southern California. For more information, publications, * and related projects, please see: http://www.isi.edu/integration ******************************************************************************/ package edu.isi.karma.kr2rml; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import org.jgrapht.graph.DirectedWeightedMultigraph; import org.jgrapht.traverse.BreadthFirstIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import edu.isi.karma.modeling.alignment.Alignment; import edu.isi.karma.modeling.alignment.GraphUtil; import edu.isi.karma.modeling.ontology.OntologyManager; import edu.isi.karma.rep.alignment.ClassInstanceLink; import edu.isi.karma.rep.alignment.ColumnNode; import edu.isi.karma.rep.alignment.ColumnSubClassLink; import edu.isi.karma.rep.alignment.DataPropertyLink; import edu.isi.karma.rep.alignment.DataPropertyOfColumnLink; import edu.isi.karma.rep.alignment.InternalNode; import edu.isi.karma.rep.alignment.Label; import edu.isi.karma.rep.alignment.Link; import edu.isi.karma.rep.alignment.LinkKeyInfo; import edu.isi.karma.rep.alignment.Node; import edu.isi.karma.rep.alignment.NodeType; import edu.isi.karma.rep.alignment.ObjectPropertySpecializationLink; import edu.isi.karma.rep.alignment.SemanticType; import edu.isi.karma.rep.alignment.SemanticTypes; import edu.isi.karma.rep.alignment.SynonymSemanticTypes; public class KR2RMLMappingGenerator { private OntologyManager ontMgr; private String sourceNamespace; // private ErrorReport errorReport; private R2RMLMapping r2rmlMapping; private final Node steinerTreeRoot; private SemanticTypes semanticTypes; private DirectedWeightedMultigraph<Node, Link> steinerTree; // Internal data structures required private Map<String, SubjectMap> subjectMapIndex; private Map<String, TriplesMap> triplesMapIndex; private KR2RMLMappingAuxillaryInformation auxInfo; private int synonymIdCounter; private final static String TRIPLES_MAP_PREFIX = "TriplesMap"; private final static String REFOBJECT_MAP_PREFIX = "RefObjectMap"; private static Logger logger = LoggerFactory.getLogger(KR2RMLMappingGenerator.class); public KR2RMLMappingGenerator(OntologyManager ontMgr, Alignment alignment, SemanticTypes semanticTypes, String sourcePrefix, String sourceNamespace, boolean generateInverse, ErrorReport errorReport) { this.ontMgr = ontMgr; // this.errorReport = errorReport; this.semanticTypes = semanticTypes; this.sourceNamespace = sourceNamespace; this.r2rmlMapping = new R2RMLMapping(); this.steinerTree = alignment.getSteinerTree(); this.steinerTreeRoot = alignment.GetTreeRoot(); this.auxInfo = new KR2RMLMappingAuxillaryInformation(); this.subjectMapIndex = new HashMap<String, SubjectMap>(); this.triplesMapIndex = new HashMap<String, TriplesMap>(); // Generate the R2RML data structures generateMappingFromSteinerTree(generateInverse); } public Node getSteinerTreeRoot() { return steinerTreeRoot; } public KR2RMLMappingAuxillaryInformation getMappingAuxillaryInformation() { return this.auxInfo; } public R2RMLMapping getR2RMLMapping() { return this.r2rmlMapping; } private void generateMappingFromSteinerTree(boolean generateInverse) { // Generate TriplesMap for each InternalNode in the tree createSubjectMaps(); // Create TripleMaps createTripleMaps(); // Identify the object property links createPredicateObjectMaps(generateInverse); // Identify blank nodes and generate backward links data structure for the triple maps identifyBlankNodes(); // Calculate the nodes covered by each InternalNode calculateColumnNodesCoveredByBlankNodes(); } private void identifyBlankNodes() { for (SubjectMap subjMap:subjectMapIndex.values()) { if (subjMap.getTemplate().getAllTerms().size() == 1 && (subjMap.getTemplate().getAllTerms().get(0) instanceof StringTemplateTerm)) { String str = subjMap.getTemplate().getAllTerms().get(0).getTemplateTermValue(); if (str.equals(sourceNamespace)) subjMap.setAsBlankNode(true); } } } private void calculateColumnNodesCoveredByBlankNodes() { Set<String> reversedLinks = new HashSet<String>(); Set<String> removedLinks = new HashSet<String>(); DirectedWeightedMultigraph<Node, Link> rootedTree = GraphUtil.treeToRootedTree(this.steinerTree, this.steinerTreeRoot, reversedLinks, removedLinks); for (Node treeNode:rootedTree.vertexSet()) { // List<Node> nodesWithSemTypesCovered = new ArrayList<Node>(); if (treeNode instanceof InternalNode && subjectMapIndex.containsKey(treeNode.getId())) { SubjectMap subjMap = subjectMapIndex.get(treeNode.getId()); if (subjMap.isBlankNode()) { List<String> hNodeIdsCovered = new ArrayList<String>(); calculateColumnNodesCoveredByNode(hNodeIdsCovered, treeNode, rootedTree); auxInfo.getBlankNodesColumnCoverage().put(treeNode.getId(), hNodeIdsCovered); auxInfo.getBlankNodesUriPrefixMap().put(treeNode.getId(), treeNode.getDisplayId()); } } } } private void calculateColumnNodesCoveredByNode(List<String> hNodeIdsCovered, Node treeNode, DirectedWeightedMultigraph<Node, Link> rootedTree) { BreadthFirstIterator<Node, Link> itr = new BreadthFirstIterator<Node, Link>(rootedTree, treeNode); while(itr.hasNext()) { Node v = itr.next(); if(v.getType() == NodeType.ColumnNode) { hNodeIdsCovered.add(v.getId()); } } while (hNodeIdsCovered.isEmpty()) { Set<Link> incomingEdges = rootedTree.incomingEdgesOf(treeNode); if (incomingEdges.isEmpty()) return; else { for (Link incomingLink: incomingEdges) { Node source = incomingLink.getSource(); calculateColumnNodesCoveredByNode(hNodeIdsCovered, source, rootedTree); if (!hNodeIdsCovered.isEmpty()) break; treeNode = source; } } } } private void createTripleMaps() { Set<Node> nodes = steinerTree.vertexSet(); for (Node node:nodes) { if (node instanceof InternalNode) { // Create a TriplesMap corresponding to the Internal node SubjectMap subjMap = subjectMapIndex.get(node.getId()); TriplesMap trMap = new TriplesMap(getNewTriplesMapId(), subjMap); triplesMapIndex.put(node.getId(), trMap); this.r2rmlMapping.addTriplesMap(trMap); } } } private void createSubjectMaps() { Set<Node> nodes = steinerTree.vertexSet(); for (Node node:nodes) { if (node instanceof InternalNode) { SubjectMap subj = new SubjectMap(node.getId()); if (node.getId().equals(steinerTreeRoot.getId())) subj.setAsSteinerTreeRootNode(true); // Add the user provided namespace as the first template term subj.getTemplate().addTemplateTermToSet(new StringTemplateTerm(sourceNamespace)); StringTemplateTerm typeTerm = new StringTemplateTerm(node.getLabel().getUri(), true); TemplateTermSet typeTermSet = new TemplateTermSet(); typeTermSet.addTemplateTermToSet(typeTerm); subj.addRdfsType(typeTermSet); subjectMapIndex.put(node.getId(), subj); Set<Link> outgoingLinks = steinerTree.outgoingEdgesOf(node); for (Link link:outgoingLinks) { if (link instanceof ClassInstanceLink || link instanceof ColumnSubClassLink || (link instanceof DataPropertyLink && link.getKeyType() == LinkKeyInfo.PartOfKey)) { Node tNode = link.getTarget(); if (tNode instanceof ColumnNode) { ColumnNode cnode = (ColumnNode) tNode; String hNodeId = cnode.getHNodeId(); ColumnTemplateTerm cnTerm = new ColumnTemplateTerm(hNodeId); // Identify classInstance links to set the template if (link instanceof ClassInstanceLink) { subj.getTemplate().clear().addTemplateTermToSet(cnTerm); } // Identify the isSubclassOfClass links to set the correct type else if (link instanceof ColumnSubClassLink) { TemplateTermSet typeTermSet2 = new TemplateTermSet(); typeTermSet2.addTemplateTermToSet(cnTerm); subj.addRdfsType(typeTermSet2); } // Identify the link which has been chosen as the key else if (link instanceof DataPropertyLink && link.getKeyType() == LinkKeyInfo.PartOfKey) { subj.getTemplate().addTemplateTermToSet(cnTerm); } } else { logger.error("Target node of Class Instance link should always be a " + "column node."); } } } } } } private void createPredicateObjectMaps(boolean generateInverse) { Set<Node> nodes = steinerTree.vertexSet(); for (Node node:nodes) { if (node instanceof InternalNode) { // Create a TriplesMap corresponding to the Internal node SubjectMap subjMap = subjectMapIndex.get(node.getId()); TriplesMap subjTrMap = triplesMapIndex.get(node.getId()); // Create the predicate object map for each outgoing link Set<Link> outgoingEdges = steinerTree.outgoingEdgesOf(node); for (Link olink:outgoingEdges) { if (olink instanceof ObjectPropertySpecializationLink || olink instanceof DataPropertyOfColumnLink || olink instanceof ColumnSubClassLink) continue; PredicateObjectMap poMap = new PredicateObjectMap(subjTrMap); Node target = olink.getTarget(); // Create an object property map if (target instanceof InternalNode) { // Get the RefObjMap object for the objectmap TriplesMap objTrMap = triplesMapIndex.get(target.getId()); RefObjectMap refObjMap = new RefObjectMap(getNewRefObjectMapId(), objTrMap); ObjectMap objMap = new ObjectMap(target.getId(), refObjMap); poMap.setObject(objMap); // Create the predicate Predicate pred = new Predicate(olink.getId()); // Check if a specialization link exists Link specializedEdge = getSpecializationLinkIfExists(olink, node); if (specializedEdge != null) { Node specializedEdgeTarget = specializedEdge.getTarget(); if (specializedEdgeTarget instanceof ColumnNode) { ColumnTemplateTerm cnTerm = new ColumnTemplateTerm( ((ColumnNode) specializedEdgeTarget).getHNodeId()); pred.getTemplate().addTemplateTermToSet(cnTerm); } } else { pred.getTemplate().addTemplateTermToSet( new StringTemplateTerm(olink.getLabel().getUri(), true)); } poMap.setPredicate(pred); if (generateInverse) addInversePropertyIfExists(subjMap, poMap, olink, subjTrMap); // Add the links in the graph links data structure TriplesMapLink link = new TriplesMapLink(subjTrMap, objTrMap, poMap); auxInfo.getTriplesMapGraph().addLink(link); } // Create a data property map else if(target instanceof ColumnNode) { // Create the object map ColumnNode cnode = (ColumnNode) target; String hNodeId = cnode.getHNodeId(); ColumnTemplateTerm cnTerm = new ColumnTemplateTerm(hNodeId); TemplateTermSet termSet = new TemplateTermSet(); termSet.addTemplateTermToSet(cnTerm); ObjectMap objMap = new ObjectMap(hNodeId, termSet); poMap.setObject(objMap); // Create the predicate Predicate pred = new Predicate(olink.getId()); // Check if a specialization link exists Link specializedEdge = getSpecializationLinkIfExists(olink, node); if (specializedEdge != null) { Node specializedEdgeTarget = specializedEdge.getTarget(); if (specializedEdgeTarget instanceof ColumnNode) { ColumnTemplateTerm cnsplTerm = new ColumnTemplateTerm( ((ColumnNode) specializedEdgeTarget).getHNodeId()); pred.getTemplate().addTemplateTermToSet(cnsplTerm); } } else { pred.getTemplate().addTemplateTermToSet( new StringTemplateTerm(olink.getLabel().getUri(), true)); } poMap.setPredicate(pred); // Save link from the HNodeId to the its PredicateObjectMap in the auxiliary information saveLinkFromHNodeIdToPredicateObjectMap(hNodeId, poMap); // Check for synonym types for this column addSynonymTypesPredicateObjectMaps(subjTrMap, hNodeId); } // Add the predicateobjectmap to the triples map after a sanity check if (poMap.getObject() != null && poMap.getPredicate() != null) subjTrMap.addPredicateObjectMap(poMap); } } } } private void saveLinkFromHNodeIdToPredicateObjectMap(String hNodeId, PredicateObjectMap poMap) { List<PredicateObjectMap> pomList = this.auxInfo.getHNodeIdToPredObjLinks().get(hNodeId); if (pomList == null) { pomList = new ArrayList<PredicateObjectMap>(); } pomList.add(poMap); this.auxInfo.getHNodeIdToPredObjLinks().put(hNodeId, pomList); } private void addSynonymTypesPredicateObjectMaps(TriplesMap subjTrMap, String hNodeId) { SynonymSemanticTypes synonyms = this.semanticTypes.getSynonymTypesForHNodeId(hNodeId); if (synonyms != null && !synonyms.getSynonyms().isEmpty()){ for (SemanticType synType:synonyms.getSynonyms()) { if (synType.isClass()) { logger.error("Synonym type as class with no property are not allowed."); continue; } PredicateObjectMap poMap = new PredicateObjectMap(subjTrMap); // Create the object map ColumnTemplateTerm cnTerm = new ColumnTemplateTerm(hNodeId); TemplateTermSet termSet = new TemplateTermSet(); termSet.addTemplateTermToSet(cnTerm); ObjectMap objMap = new ObjectMap(hNodeId, termSet); poMap.setObject(objMap); // Create the predicate Predicate pred = new Predicate(synType.getType().getUri() + "-synonym" + getNewSynonymIdCount()); pred.getTemplate().addTemplateTermToSet( new StringTemplateTerm(synType.getType().getUri(), true)); poMap.setPredicate(pred); // Add the predicate object map to the triples map subjTrMap.addPredicateObjectMap(poMap); // Save the link from hNodeId to PredicateObjectMap saveLinkFromHNodeIdToPredicateObjectMap(hNodeId, poMap); } } } private int getNewSynonymIdCount() { return synonymIdCounter++; } private void addInversePropertyIfExists(SubjectMap subjMap, PredicateObjectMap poMap, Link olink, TriplesMap subjTrMap) { String propUri = olink.getLabel().getUri(); //this can happen if propertyName is not an Object property; it could be a subclass if (!ontMgr.isObjectProperty(propUri)) return; Label inversePropLabel = ontMgr.getInverseProperty(propUri); Label inverseOfPropLabel = ontMgr.getInverseOfProperty(propUri); if (inversePropLabel != null) { TriplesMap inverseTrMap = triplesMapIndex.get(poMap.getObject().getId()); // Create the predicate object map PredicateObjectMap invPoMap = new PredicateObjectMap(inverseTrMap); // Create the predicate Predicate pred = new Predicate(olink.getId()+"+inverse"); pred.getTemplate().addTemplateTermToSet( new StringTemplateTerm(inversePropLabel.getUri(), true)); invPoMap.setPredicate(pred); // Create the object using RefObjMap RefObjectMap refObjMap = new RefObjectMap(getNewRefObjectMapId(), subjTrMap); ObjectMap invObjMap = new ObjectMap(subjMap.getId(), refObjMap); invPoMap.setObject(invObjMap); inverseTrMap.addPredicateObjectMap(invPoMap); // Add the link to the link set auxInfo.getTriplesMapGraph().addLink(new TriplesMapLink(inverseTrMap, subjTrMap, invPoMap)); } if (inverseOfPropLabel != null) { // Create the triples map // Get the object's triples map TriplesMap inverseOfTrMap = triplesMapIndex.get(poMap.getObject().getId()); PredicateObjectMap invOfPoMap = new PredicateObjectMap(inverseOfTrMap); // Create the predicate Predicate pred = new Predicate(olink.getId()+"+inverseOf"); pred.getTemplate().addTemplateTermToSet( new StringTemplateTerm(inverseOfPropLabel.getUri(), true)); invOfPoMap.setPredicate(pred); // Create the object using RefObjMap RefObjectMap refObjMap = new RefObjectMap(getNewRefObjectMapId(), subjTrMap); ObjectMap invOfObjMap = new ObjectMap(subjMap.getId(), refObjMap); invOfPoMap.setObject(invOfObjMap); inverseOfTrMap.addPredicateObjectMap(invOfPoMap); // Add the link to the link set auxInfo.getTriplesMapGraph().addLink(new TriplesMapLink(inverseOfTrMap, subjTrMap, invOfPoMap)); } } private Link getSpecializationLinkIfExists(Link link, Node sourceNode) { Set<Link> outgoingEdges = this.steinerTree.outgoingEdgesOf(sourceNode); for (Link olink:outgoingEdges) { // Check for the object property specialization if (olink instanceof ObjectPropertySpecializationLink ) { ObjectPropertySpecializationLink splLink = (ObjectPropertySpecializationLink) olink; if (splLink.getSpecializedLink().getId().equals(link.getId())) return olink; } // Check for the data property specialization else if (olink instanceof DataPropertyOfColumnLink) { DataPropertyOfColumnLink dlink = (DataPropertyOfColumnLink) olink; Node target = link.getTarget(); if (target instanceof ColumnNode) { ColumnNode cnode = (ColumnNode) target; if (dlink.getSpecializedColumnHNodeId().equals(cnode.getId())) return dlink; } } } return null; } private String getNewRefObjectMapId() { return REFOBJECT_MAP_PREFIX + "_" + UUID.randomUUID(); } private String getNewTriplesMapId() { return TRIPLES_MAP_PREFIX + "_" + UUID.randomUUID(); } public TriplesMap getTriplesMapForNodeId(String nodeId) { return this.triplesMapIndex.get(nodeId); } }