/******************************************************************************* * 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.modeling.alignment; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.log4j.Logger; import org.jgrapht.UndirectedGraph; import org.jgrapht.graph.DirectedWeightedMultigraph; import org.jgrapht.graph.WeightedMultigraph; import com.rits.cloning.Cloner; import edu.isi.karma.modeling.Uris; import edu.isi.karma.modeling.ontology.OntologyManager; import edu.isi.karma.modeling.ontology.OntologyUpdateListener; 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.LinkStatus; import edu.isi.karma.rep.alignment.LinkType; import edu.isi.karma.rep.alignment.Node; import edu.isi.karma.rep.alignment.NodeType; import edu.isi.karma.rep.alignment.ObjectPropertyLink; import edu.isi.karma.rep.alignment.ObjectPropertySpecializationLink; import edu.isi.karma.rep.alignment.SubClassLink; public class Alignment implements OntologyUpdateListener { static Logger logger = Logger.getLogger(Alignment.class); private GraphBuilder graphBuilder; private DirectedWeightedMultigraph<Node, Link> steinerTree = null; private Node root = null; private NodeIdFactory nodeIdFactory; // private LinkIdFactory linkIdFactory; public Alignment(OntologyManager ontologyManager) { this.nodeIdFactory = new NodeIdFactory(); // this.linkIdFactory = new LinkIdFactory(); ontologyManager.subscribeListener(this); logger.info("building initial graph ..."); graphBuilder = new GraphBuilder(ontologyManager, nodeIdFactory);//, linkIdFactory); } public boolean isEmpty() { return (this.graphBuilder.getGraph().edgeSet().size() == 0); } public Node GetTreeRoot() { return this.root; } public Alignment getAlignmentClone() { Cloner cloner = new Cloner(); // cloner.setDumpClonedClasses(true); cloner.dontClone(OntologyManager.class); cloner.dontCloneInstanceOf(OntologyManager.class); cloner.dontClone(DirectedWeightedMultigraph.class); cloner.dontCloneInstanceOf(DirectedWeightedMultigraph.class); return cloner.deepClone(this); } public DirectedWeightedMultigraph<Node, Link> getSteinerTree() { if (this.steinerTree == null) align(); // GraphUtil.printGraph(this.steinerTree); return this.steinerTree; } public DirectedWeightedMultigraph<Node, Link> getGraph() { return this.graphBuilder.getGraph(); } public void setGraph(DirectedWeightedMultigraph<Node, Link> graph) { this.graphBuilder.setGraph(graph); } public Set<Node> getGraphNodes() { return this.graphBuilder.getGraph().vertexSet(); } public Set<Link> getGraphLinks() { return this.graphBuilder.getGraph().edgeSet(); } public Node getNodeById(String nodeId) { return this.graphBuilder.getIdToNodeMap().get(nodeId); } public List<Node> getNodesByUri(String uriString) { return this.graphBuilder.getUriToNodesMap().get(uriString); } public List<Node> getNodesByType(NodeType type) { return this.graphBuilder.getTypeToNodesMap().get(type); } public Link getLinkById(String linkId) { return this.graphBuilder.getIdToLinkMap().get(linkId); } public List<Link> getLinksByUri(String uriString) { return this.graphBuilder.getUriToLinksMap().get(uriString); } public List<Link> getLinksByType(LinkType type) { return this.graphBuilder.getTypeToLinksMap().get(type); } public List<Link> getLinksByStatus(LinkStatus status) { return this.graphBuilder.getStatusToLinksMap().get(status); } public int getLastIndexOfNodeUri(String uri) { return this.nodeIdFactory.lastIndexOf(uri); } // public int getLastIndexOfLinkUri(String uri) { // return this.linkIdFactory.lastIndexOf(uri); // } public ColumnNode getColumnNodeByHNodeId(String hNodeId) { List<Node> columnNodes = this.getNodesByType(NodeType.ColumnNode); if (columnNodes == null) return null; for (Node cNode : columnNodes) { if (((ColumnNode)cNode).getHNodeId().equals(hNodeId)) return (ColumnNode)cNode; } return null; } // AddNode methods public ColumnNode addColumnNode(String hNodeId, String columnName, String rdfLiteralType) { // use hNodeId as id of the node ColumnNode node = new ColumnNode(hNodeId, hNodeId, columnName, rdfLiteralType); if (this.graphBuilder.addNode(node)) return node; return null; } public InternalNode addInternalNode(Label label) { String id = nodeIdFactory.getNodeId(label.getUri()); InternalNode node = new InternalNode(id, label); if (this.graphBuilder.addNode(node)) return node; return null; } public ColumnNode addColumnNodeWithoutUpdatingGraph(String hNodeId, String columnName, String rdfLiteralType) { // use hNodeId as id of the node ColumnNode node = new ColumnNode(hNodeId, hNodeId, columnName, rdfLiteralType); if (this.graphBuilder.addNodeWithoutUpdatingGraph(node)) return node; return null; } public InternalNode addInternalNodeWithoutUpdatingGraph(Label label) { String id = nodeIdFactory.getNodeId(label.getUri()); InternalNode node = new InternalNode(id, label); if (this.graphBuilder.addNodeWithoutUpdatingGraph(node)) return node; return null; } public void updateGraph() { this.graphBuilder.updateGraph(); } public ColumnNode createColumnNode(String hNodeId, String columnName, String rdfLiteralType) { // use hNodeId as id of the node ColumnNode node = new ColumnNode(hNodeId, hNodeId, columnName, rdfLiteralType); return node; } public InternalNode createInternalNode(Label label) { String id = nodeIdFactory.getNodeId(label.getUri()); InternalNode node = new InternalNode(id, label); return node; } public void addNodeList(List<Node> nodes) { this.graphBuilder.addNodeList(nodes); } // AddLink methods public DataPropertyLink addDataPropertyLink(Node source, Node target, Label label, boolean partOfKey) { String id = LinkIdFactory.getLinkId(label.getUri(), source.getId(), target.getId()); DataPropertyLink link = new DataPropertyLink(id, label, partOfKey); if (this.graphBuilder.addLink(source, target, link)) return link; return null; } // Probably we don't need this function in the interface to GUI public ObjectPropertyLink addObjectPropertyLink(Node source, Node target, Label label) { String id = LinkIdFactory.getLinkId(label.getUri(), source.getId(), target.getId()); ObjectPropertyLink link = new ObjectPropertyLink(id, label); if (this.graphBuilder.addLink(source, target, link)) return link; return null; } // Probably we don't need this function in the interface to GUI public SubClassLink addSubClassOfLink(Node source, Node target) { String id = LinkIdFactory.getLinkId(Uris.RDFS_SUBCLASS_URI, source.getId(), target.getId()); SubClassLink link = new SubClassLink(id); if (this.graphBuilder.addLink(source, target, link)) return link; return null; } public ClassInstanceLink addClassInstanceLink(Node source, Node target, LinkKeyInfo keyInfo) { String id = LinkIdFactory.getLinkId(Uris.CLASS_INSTANCE_LINK_URI, source.getId(), target.getId()); ClassInstanceLink link = new ClassInstanceLink(id, keyInfo); if (this.graphBuilder.addLink(source, target, link)) return link; return null; } public DataPropertyOfColumnLink addDataPropertyOfColumnLink(Node source, Node target, String specializedColumnHNodeId) { String id = LinkIdFactory.getLinkId(Uris.DATAPROPERTY_OF_COLUMN_LINK_URI, source.getId(), target.getId()); DataPropertyOfColumnLink link = new DataPropertyOfColumnLink(id, specializedColumnHNodeId); if (this.graphBuilder.addLink(source, target, link)) return link; return null; } public ObjectPropertySpecializationLink addObjectPropertySpecializationLink(Node source, Node target, Link specializedLink) { String id = LinkIdFactory.getLinkId(Uris.OBJECTPROPERTY_SPECIALIZATION_LINK_URI, source.getId(), target.getId()); ObjectPropertySpecializationLink link = new ObjectPropertySpecializationLink(id, specializedLink); if (this.graphBuilder.addLink(source, target, link)) return link; return null; } public ColumnSubClassLink addColumnSubClassOfLink(Node source, Node target) { String id = LinkIdFactory.getLinkId(Uris.COLUMN_SUBCLASS_LINK_URI, source.getId(), target.getId()); ColumnSubClassLink link = new ColumnSubClassLink(id); if (this.graphBuilder.addLink(source, target, link)) return link; return null; } public void changeLinkWeight(String linkId, double weight) { Link link = this.getLinkById(linkId); if (link == null) { logger.error("Could not find the link with the id " + linkId); return; } this.graphBuilder.changeLinkWeight(link, weight); } public void changeLinkStatus(String linkId, LinkStatus newStatus) { // if (linkId.equals("http://km.aifb.kit.edu/projects/d3/cruiser#Vehicle1---http://km.aifb.kit.edu/projects/d3/cruiser#at---http://www.w3.org/2003/01/geo/wgs84_pos#Point1")) // System.out.println("debug1"); // if (linkId.equals("http://km.aifb.kit.edu/projects/d3/cruiser#Observation1---http://km.aifb.kit.edu/projects/d3/cruiser#at---http://www.w3.org/2003/01/geo/wgs84_pos#Point1")) // System.out.println("debug2"); logger.debug("changing the status of link " + linkId + " to " + newStatus.name()); Link link = this.getLinkById(linkId); if (link == null) { if (newStatus == LinkStatus.ForcedByUser) { Node source = this.getNodeById(LinkIdFactory.getLinkSourceId(linkId)); Node target = this.getNodeById(LinkIdFactory.getLinkTargetId(linkId)); String linkUri = LinkIdFactory.getLinkUri(linkId); Link newLink; if (linkUri.equalsIgnoreCase(Uris.RDFS_SUBCLASS_URI)) newLink = new SubClassLink(linkId); else newLink = new ObjectPropertyLink(linkId, this.graphBuilder.getOntologyManager().getUriLabel(linkUri)); newLink.setStatus(LinkStatus.ForcedByUser); this.graphBuilder.addLink(source, target, newLink); } } else this.graphBuilder.changeLinkStatus(link, newStatus); } /** * This method removes a node from the graph and also all the links and the nodes that * are added to the graph by adding the specified node. * This method is useful when the user changes the semantic type assigned to a column. * The GUI needs to call the method by sending a Column Node * @param nodeId */ public boolean removeNode(String nodeId) { Node node = this.getNodeById(nodeId); if (node == null) { logger.debug("Cannot find the node " + nodeId + " in the graph."); return false; } this.graphBuilder.removeNode(node); return false; } /** * This method just deletes the specified link from the graph and leaves the rest of the graph untouched. * Probably we don't need to use this function. * @param linkId * @return */ public boolean removeLink(String linkId) { Link link = this.getLinkById(linkId); if (link != null) return this.graphBuilder.removeLink(link); logger.debug("Cannot find the link " + linkId + " in the graph."); return false; } public Set<Link> getCurrentIncomingLinksToNode(String nodeId) { Node node = this.getNodeById(nodeId); if (node == null) return null; if (!this.steinerTree.containsVertex(node)) return null; return this.steinerTree.incomingEdgesOf(node); } public Set<Link> getCurrentOutgoingLinksToNode(String nodeId) { Node node = this.getNodeById(nodeId); if (node == null) return null; if (!this.steinerTree.containsVertex(node)) return null; return this.steinerTree.outgoingEdgesOf(node); } public List<Link> getLinks(String sourceId, String targetId) { return this.graphBuilder.getPossibleLinks(sourceId, targetId); } public List<Link> getIncomingLinks(String nodeId) { List<Link> possibleLinks = new ArrayList<Link>(); List<Link> temp; HashSet<Link> allLinks = new HashSet<Link>(); Node node = this.getNodeById(nodeId); if (node == null) return possibleLinks; Set<Link> incomingLinks = this.graphBuilder.getGraph().incomingEdgesOf(node); if (incomingLinks != null) { temp = Arrays.asList(incomingLinks.toArray(new Link[0])); allLinks.addAll(temp); } Set<Link> outgoingLinks = this.graphBuilder.getGraph().outgoingEdgesOf(node); if (outgoingLinks != null) { temp = Arrays.asList(outgoingLinks.toArray(new Link[0])); allLinks.addAll(outgoingLinks); } if (allLinks.size() == 0) return possibleLinks; String sourceId, targetId; for (Link e : allLinks) { if (e.getSource().getId().equals(nodeId)) { // outgoing link sourceId = e.getTarget().getId(); targetId = nodeId; } else { // incoming link sourceId = e.getSource().getId(); targetId = nodeId; } temp = getLinks(sourceId, targetId); if (temp != null) possibleLinks.addAll(temp); } Collections.sort(possibleLinks); // for (Link l : possibleLinks) { // System.out.print(l.getId() + " === "); // System.out.print(l.getSource().getId() + " === "); // System.out.print(l.getSource().getLabel().getNs() + " === "); // System.out.print(l.getSource().getLocalId() + " === "); // System.out.println(l.getSource().getDisplayId()); // System.out.print(l.getTarget().getId() + " === "); // System.out.println(l.getLabel().getUri() + " === "); // } logger.info("Finished obtaining the incoming links."); return possibleLinks; } public List<Link> getOutgoingLinks(String nodeId) { List<Link> possibleLinks = new ArrayList<Link>(); List<Link> temp; HashSet<Link> allLinks = new HashSet<Link>(); Node node = this.getNodeById(nodeId); if (node == null) return possibleLinks; Set<Link> incomingLinks = this.graphBuilder.getGraph().incomingEdgesOf(node); if (incomingLinks != null) { temp = Arrays.asList(incomingLinks.toArray(new Link[0])); allLinks.addAll(temp); } Set<Link> outgoingLinks = this.graphBuilder.getGraph().outgoingEdgesOf(node); if (outgoingLinks != null) { temp = Arrays.asList(outgoingLinks.toArray(new Link[0])); allLinks.addAll(outgoingLinks); } if (allLinks.size() == 0) return possibleLinks; String sourceId, targetId; for (Link e : allLinks) { if (e.getSource().getId().equals(nodeId)) { // outgoing link sourceId = nodeId; targetId = e.getTarget().getId(); } else { // incoming link sourceId = nodeId; targetId = e.getSource().getId(); } temp = getLinks(sourceId, targetId); if (temp != null) possibleLinks.addAll(temp); } Collections.sort(possibleLinks); logger.info("Finished obtaining the outgoing links."); return possibleLinks; } private void updateLinksPreferredByUI() { if (this.steinerTree == null) return; // Change the status of previously preferred links to normal List<Link> linksInPreviousTree = this.getLinksByStatus(LinkStatus.PreferredByUI); if (linksInPreviousTree != null) { Link[] links = linksInPreviousTree.toArray(new Link[0]); for (Link link : links) this.graphBuilder.changeLinkStatus(link, LinkStatus.Normal); } List<Link> linksForcedByUser = this.getLinksByStatus(LinkStatus.ForcedByUser); for (Link link: this.steinerTree.edgeSet()) { if (linksForcedByUser == null || !linksForcedByUser.contains(link)) { this.graphBuilder.changeLinkStatus(link, LinkStatus.PreferredByUI); logger.debug("link " + link.getId() + " has been added to preferred UI links."); } } } // FIXME: What if a column node has more than one domain? private List<Node> computeSteinerNodes() { List<Node> steinerNodes = new ArrayList<Node>(); // Add column nodes and their domain List<Node> columnNodes = this.getNodesByType(NodeType.ColumnNode); if (columnNodes != null) { for (Node n : columnNodes) { Set<Link> incomingLinks = this.graphBuilder.getGraph().incomingEdgesOf(n); if (incomingLinks != null && incomingLinks.size() == 1) { Node domain = incomingLinks.toArray(new Link[0])[0].getSource(); // adding the column node steinerNodes.add(n); // adding the domain steinerNodes.add(domain); } else logger.error("The column node " + n.getId() + " does not have any domain or it has more than one domain."); } } // Add source and target of the links forced by the user List<Link> linksForcedByUser = this.getLinksByStatus(LinkStatus.ForcedByUser); if (linksForcedByUser != null) { for (Link link : linksForcedByUser) { if (!steinerNodes.contains(link.getSource())) steinerNodes.add(link.getSource()); if (!steinerNodes.contains(link.getTarget())) steinerNodes.add(link.getTarget()); } } return steinerNodes; } public void align() { // System.out.println("*** Graph ***"); // GraphUtil.printGraphSimple(this.graphBuilder.getGraph()); long start = System.currentTimeMillis(); logger.info("updating UI preferred links ..."); this.updateLinksPreferredByUI(); logger.info("forced links ..."); if (this.getLinksByStatus(LinkStatus.ForcedByUser) != null) { for (Link link: this.getLinksByStatus(LinkStatus.ForcedByUser)) System.out.println("\t" + link.getId()); } logger.info("preparing G Prime for steiner algorithm input ..."); GraphPreProcess graphPreProcess = new GraphPreProcess(this.graphBuilder.getGraph(), this.getLinksByStatus(LinkStatus.PreferredByUI), this.getLinksByStatus(LinkStatus.ForcedByUser)); UndirectedGraph<Node, Link> undirectedGraph = graphPreProcess.getUndirectedGraph(); logger.info("computing steiner nodes ..."); List<Node> steinerNodes = this.computeSteinerNodes(); logger.info("steiner nodes ..."); if (steinerNodes != null) { for (Node node: steinerNodes) System.out.println("\t" + node.getId()); } logger.info("computing steiner tree ..."); SteinerTree steinerTree = new SteinerTree(undirectedGraph, steinerNodes); WeightedMultigraph<Node, Link> tree = steinerTree.getSteinerTree(); if (tree == null) { logger.info("resulting tree is null ..."); return; } System.out.println("*** steiner tree before post processing step ***"); GraphUtil.printGraphSimple(tree); // logger.info("selecting a root for the tree ..."); TreePostProcess treePostProcess = new TreePostProcess(this.graphBuilder, tree, getLinksByStatus(LinkStatus.ForcedByUser), this.graphBuilder.getThingNode()); this.steinerTree = treePostProcess.getTree(); this.root = treePostProcess.getRoot(); System.out.println("*** steiner tree after post processing step ***"); GraphUtil.printGraphSimple(this.steinerTree); long elapsedTimeMillis = System.currentTimeMillis() - start; float elapsedTimeSec = elapsedTimeMillis/1000F; logger.info("total number of nodes in steiner tree: " + this.steinerTree.vertexSet().size()); logger.info("total number of edges in steiner tree: " + this.steinerTree.edgeSet().size()); logger.info("time to compute steiner tree: " + elapsedTimeSec); } @Override public void ontologyModelUpdated() { this.graphBuilder.resetOntologyMaps(); } }