package edu.isi.karma.controller.update; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import org.jgrapht.alg.DijkstraShortestPath; import org.jgrapht.graph.DirectedWeightedMultigraph; import org.jgrapht.traverse.BreadthFirstIterator; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import edu.isi.karma.modeling.alignment.Alignment; import edu.isi.karma.modeling.alignment.AlignmentManager; import edu.isi.karma.modeling.alignment.GraphUtil; import edu.isi.karma.rep.HNodePath; import edu.isi.karma.rep.alignment.ColumnNode; import edu.isi.karma.rep.alignment.DataPropertyOfColumnLink; import edu.isi.karma.rep.alignment.Link; import edu.isi.karma.rep.alignment.LinkKeyInfo; 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.ObjectPropertySpecializationLink; import edu.isi.karma.view.VWorksheet; import edu.isi.karma.view.VWorkspace; public class SVGAlignmentUpdate_ForceKarmaLayout extends AbstractUpdate { private final VWorksheet vWorksheet; private final DirectedWeightedMultigraph<Node, Link> tree; private final Node root; private static Logger logger = LoggerFactory.getLogger(SVGAlignmentUpdate_ForceKarmaLayout.class); private enum JsonKeys { worksheetId, alignmentId, label, id, hNodeId, nodeType, source, target, linkType, sourceNodeId, targetNodeId, height, hNodesCovered, nodes, links, maxTreeHeight, linkStatus, linkUri } private enum JsonValues { key, holderLink, objPropertyLink, Unassigned, FakeRoot, FakeRootLink, Add_Parent, DataPropertyOfColumnHolder, horizontalDataPropertyLink } public SVGAlignmentUpdate_ForceKarmaLayout(VWorksheet vWorksheet, Alignment alignment) { super(); this.vWorksheet = vWorksheet; this.tree = alignment.getSteinerTree(); this.root = alignment.GetTreeRoot(); } @Override public void generateJson(String prefix, PrintWriter pw, VWorkspace vWorkspace) { List<String> hNodeIdList = new ArrayList<String>(); List<HNodePath> columns = vWorksheet.getColumns(); for(HNodePath path:columns) hNodeIdList.add(path.getLeaf().getId()); String alignmentId = AlignmentManager.Instance().constructAlignmentId( vWorkspace.getWorkspace().getId(), vWorksheet.getId()); JSONObject topObj = new JSONObject(); try { topObj.put(GenericJsonKeys.updateType.name(), SVGAlignmentUpdate_ForceKarmaLayout.class.getSimpleName()); topObj.put(JsonKeys.alignmentId.name(), alignmentId); topObj.put(JsonKeys.worksheetId.name(), vWorksheet.getId()); // Reversing the inverse links for easy traversal through graph Set<String> reversedLinks = new HashSet<String>(); Set<String> removedLinks = new HashSet<String>(); DirectedWeightedMultigraph<Node, Link> rootedTree = GraphUtil.treeToRootedTree( tree, this.root, reversedLinks, removedLinks); GraphUtil.printGraphSimple(rootedTree); /*** Add the nodes and the links from the Steiner tree ***/ List<String> hNodeIdsAdded = new ArrayList<String>(); JSONArray nodesArr = new JSONArray(); JSONArray linksArr = new JSONArray(); int maxTreeHeight = 0; if (rootedTree != null && rootedTree.vertexSet().size() != 0) { /** Add the nodes **/ Set<Node> nodes = rootedTree.vertexSet(); HashMap<Node, Integer> verticesIndex = new HashMap<Node, Integer>(); int nodesIndexcounter = 0; for (Node node : nodes) { /** Get info about the nodes that this node covers or sits above **/ List<Node> nodesWithSemTypesCovered = new ArrayList<Node>(); int height = getHeight(node, nodesWithSemTypesCovered, rootedTree); if(height >= maxTreeHeight) { maxTreeHeight = height; } /** Add the hnode ids of the columns that this vertex covers **/ JSONArray hNodeIdsCoveredByVertex = new JSONArray(); for(Node v : nodesWithSemTypesCovered) { if (v instanceof ColumnNode) { ColumnNode cNode = (ColumnNode) v; hNodeIdsCoveredByVertex.put(cNode.getHNodeId()); } } String hNodeId = ""; /** Add the semantic type information **/ if (node instanceof ColumnNode) { ColumnNode cNode = (ColumnNode) node; hNodeId = cNode.getHNodeId(); hNodeIdsAdded.add(cNode.getHNodeId()); } JSONObject nodeObj = getNodeJsonObject(node.getLocalId(), node.getId(), node.getType().name() , height, hNodeIdsCoveredByVertex, hNodeId); nodesArr.put(nodeObj); verticesIndex.put(node, nodesIndexcounter++); } /*** Add the links ***/ Set<Link> links = rootedTree.edgeSet(); for (Link link : links) { Node source = link.getSource(); Integer sourceIndex = verticesIndex.get(source); Node target = link.getTarget(); Integer targetIndex = verticesIndex.get(target); Set<Link> outEdges = rootedTree.outgoingEdgesOf(target); if(sourceIndex == null || targetIndex == null) { logger.error("Edge vertex index not found!"); continue; } JSONObject linkObj = new JSONObject(); if (reversedLinks.contains(link.getId())) { linkObj.put(JsonKeys.source.name(), targetIndex); linkObj.put(JsonKeys.target.name(), sourceIndex); linkObj.put(JsonKeys.sourceNodeId.name(), target.getId()); linkObj.put(JsonKeys.targetNodeId.name(), source.getId()); } else { linkObj.put(JsonKeys.source.name(), sourceIndex); linkObj.put(JsonKeys.target.name(), targetIndex); linkObj.put(JsonKeys.sourceNodeId.name(), source.getId()); linkObj.put(JsonKeys.targetNodeId.name(), target.getId()); } linkObj.put(JsonKeys.label.name(), link.getLabel().getLocalName()); linkObj.put(JsonKeys.id.name(), link.getId()+""); linkObj.put(JsonKeys.linkStatus.name(), link.getStatus().name()); linkObj.put(JsonKeys.linkUri.name(), link.getLabel().getUri()); if(target.getType() == NodeType.ColumnNode && outEdges.isEmpty()) { linkObj.put(JsonKeys.linkType.name(), JsonValues.holderLink.name()); if(link.getKeyType() == LinkKeyInfo.PartOfKey) linkObj.put(JsonKeys.label.name(), link.getLabel().getLocalName()+"*"); } linksArr.put(linkObj); if (link.getType() == LinkType.ClassInstanceLink && link.getKeyType() == LinkKeyInfo.PartOfKey && target instanceof ColumnNode) { ColumnNode cNode = (ColumnNode) target; // Add the holder vertex object and the link that attaches nodes to the columns JSONArray hNodeIdsCoveredByVertex_holder = new JSONArray(); hNodeIdsCoveredByVertex_holder.put(cNode.getHNodeId()); JSONObject vertObj_holder = getNodeJsonObject(JsonValues.key.name(), source.getId()+"_holder" , NodeType.ColumnNode.name(), 0, hNodeIdsCoveredByVertex_holder, cNode.getHNodeId()); nodesArr.put(vertObj_holder); nodesIndexcounter++; // Add the holder link JSONObject linkObj_holder = getLinkJsonObject(JsonValues.key.name(), "" , nodesIndexcounter, nodesIndexcounter-1, "", "", "", ""); linksArr.put(linkObj_holder); } if (link.getType() == LinkType.DataPropertyOfColumnLink) { DataPropertyOfColumnLink dpLink = (DataPropertyOfColumnLink)link; String startHNodeId = dpLink.getSpecializedColumnHNodeId(); // Get height of the class instance node List<Node> nodesWithSemTypesCovered = new ArrayList<Node>(); int height = getHeight(link.getSource(), nodesWithSemTypesCovered, rootedTree); // Add 2 more holder nodes // Start node JSONArray hNodeIdsCoveredByVertex_holder = new JSONArray(); hNodeIdsCoveredByVertex_holder.put(startHNodeId); JSONObject startNode = getNodeJsonObject("", source.getId()+"_holder" , JsonValues.DataPropertyOfColumnHolder.name() , height-0.35, hNodeIdsCoveredByVertex_holder, startHNodeId); nodesArr.put(startNode); nodesIndexcounter++; // End node String endHNodeId = ((ColumnNode)link.getTarget()).getHNodeId(); JSONArray hNodeIdsCoveredByVertex_holder_2 = new JSONArray(); hNodeIdsCoveredByVertex_holder_2.put(endHNodeId); JSONObject endNode = getNodeJsonObject("", target.getId()+"_holder", JsonValues.DataPropertyOfColumnHolder.name(), height-0.35, hNodeIdsCoveredByVertex_holder_2, endHNodeId); nodesArr.put(endNode); nodesIndexcounter++; // Add the horizontal link JSONObject linkObj_holder = getLinkJsonObject("", "", nodesIndexcounter-2, nodesIndexcounter-1, JsonValues.horizontalDataPropertyLink.name(), "", "", ""); linksArr.put(linkObj_holder); } else if (link.getType() == LinkType.ObjectPropertySpecializationLink) { ObjectPropertySpecializationLink opLink = (ObjectPropertySpecializationLink)link; Link specializedLink = opLink.getSpecializedLink(); // Get height of the class instance node List<Node> nodesWithSemTypesCovered = new ArrayList<Node>(); int height = getHeight(specializedLink.getTarget(), nodesWithSemTypesCovered, rootedTree); // Add 2 more holder nodes // Start node JSONArray hNodeIdsCoveredByVertex_holder = new JSONArray(); for(Node v : nodesWithSemTypesCovered) { if (v instanceof ColumnNode) { ColumnNode cNode = (ColumnNode) v; hNodeIdsCoveredByVertex_holder.put(cNode.getHNodeId()); } } JSONObject startNode = getNodeJsonObject("", source.getId()+"_holder", JsonValues.DataPropertyOfColumnHolder.name(), height+0.65, hNodeIdsCoveredByVertex_holder, ""); nodesArr.put(startNode); nodesIndexcounter++; // End node String endHNodeId = ((ColumnNode)link.getTarget()).getHNodeId(); JSONArray hNodeIdsCoveredByVertex_holder_2 = new JSONArray(); hNodeIdsCoveredByVertex_holder_2.put(endHNodeId); JSONObject endNode = getNodeJsonObject("", target.getId()+"_holder", JsonValues.DataPropertyOfColumnHolder.name(), height+0.65, hNodeIdsCoveredByVertex_holder_2, endHNodeId); nodesArr.put(endNode); nodesIndexcounter++; // Add the horizontal link JSONObject linkObj_holder = getLinkJsonObject("", "", nodesIndexcounter-2, nodesIndexcounter-1, JsonValues.horizontalDataPropertyLink.name(), "", "", ""); linksArr.put(linkObj_holder); } linkObj.put(JsonKeys.linkType.name(), link.getType()); } } // Add the vertices for the columns that were not in Steiner tree hNodeIdList.removeAll(hNodeIdsAdded); for(String hNodeId : hNodeIdList) { JSONArray hNodeIdsCoveredByVertex = new JSONArray(); hNodeIdsCoveredByVertex.put(hNodeId); JSONObject vertObj = getNodeJsonObject("", hNodeId, JsonValues.Unassigned.name() , 0, hNodeIdsCoveredByVertex, hNodeId); nodesArr.put(vertObj); } topObj.put(JsonKeys.maxTreeHeight.name(), maxTreeHeight); topObj.put(JsonKeys.nodes.name(), nodesArr); topObj.put(JsonKeys.links.name(), linksArr); pw.write(topObj.toString()); } catch (JSONException e) { logger.error("Error occured while writing JSON!", e); } } private int getHeight(Node vertex, List<Node> nodesWithSemTypesCovered, DirectedWeightedMultigraph<Node, Link> treeClone) { BreadthFirstIterator<Node, Link> itr = new BreadthFirstIterator<Node, Link>(treeClone, vertex); Node lastNodeWithSemanticType = null; int height = 0; while(itr.hasNext()) { Node v = itr.next(); if(v.getType() == NodeType.ColumnNode) { lastNodeWithSemanticType = v; nodesWithSemTypesCovered.add(v); } } if(lastNodeWithSemanticType != null) { height = new DijkstraShortestPath<Node, Link>(treeClone, vertex, lastNodeWithSemanticType).getPathEdgeList().size(); if(lastNodeWithSemanticType.getType() == NodeType.InternalNode && lastNodeWithSemanticType.getType() == NodeType.ColumnNode && vertex != lastNodeWithSemanticType) { height += getHeight(lastNodeWithSemanticType, new ArrayList<Node>(), treeClone); } if(vertex == lastNodeWithSemanticType && vertex.getType() == NodeType.InternalNode && vertex.getType() == NodeType.ColumnNode) { height = 1; } } else { while (nodesWithSemTypesCovered.isEmpty()) { Set<Link> incomingEdges = treeClone.incomingEdgesOf(vertex); if (incomingEdges.isEmpty()) return height; else { for (Link incomingLink: incomingEdges) { Node source = incomingLink.getSource(); height = getHeight(source, nodesWithSemTypesCovered, treeClone) + 1; if (!nodesWithSemTypesCovered.isEmpty()) break; vertex = source; } } } } return height; } private JSONObject getNodeJsonObject(String label, String id, String nodeType , double height, JSONArray hNodeIdsCoveredByVertex, String hNodeId) throws JSONException { JSONObject nodeObj = new JSONObject(); nodeObj.put(JsonKeys.label.name(), label); nodeObj.put(JsonKeys.id.name(), id); nodeObj.put(JsonKeys.nodeType.name(), nodeType); nodeObj.put(JsonKeys.height.name(), height); nodeObj.put(JsonKeys.hNodesCovered.name(), hNodeIdsCoveredByVertex); if (!hNodeId.equals("")) nodeObj.put(JsonKeys.hNodeId.name(), hNodeId); return nodeObj; } private JSONObject getLinkJsonObject(String label, String id, int sourceIndex, int targetIndex , String linkType, String sourceNodeId, String targetNodeId, String linkStatus) throws JSONException { JSONObject linkObj = new JSONObject(); linkObj.put(JsonKeys.label.name(), label); linkObj.put(JsonKeys.id.name(), id); linkObj.put(JsonKeys.source.name(), sourceIndex); linkObj.put(JsonKeys.target.name(), targetIndex); linkObj.put(JsonKeys.linkType.name(), linkType); if (!sourceNodeId.equals("")) linkObj.put(JsonKeys.sourceNodeId.name(), sourceNodeId); if (!targetNodeId.equals("")) linkObj.put(JsonKeys.targetNodeId.name(), targetNodeId); if (!linkStatus.equals("")) linkObj.put(JsonKeys.linkStatus.name(), linkStatus); return linkObj; } }