/******************************************************************************* * 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.view.alignmentHeadings; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.jgrapht.graph.DirectedWeightedMultigraph; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import edu.isi.karma.rep.HNode; import edu.isi.karma.rep.alignment.Link; import edu.isi.karma.rep.alignment.Node; import edu.isi.karma.rep.hierarchicalheadings.TForest; import edu.isi.karma.rep.hierarchicalheadings.TNode; public class AlignmentForest implements TForest { private List<TNode> roots = new ArrayList<TNode>(); private Map<TNode, HNode> TNodeToHNodeMap = new HashMap<TNode, HNode>(); private static Logger logger = LoggerFactory .getLogger(AlignmentForest.class.getName()); public List<TNode> getRoots() { return roots; } public void addRoot(TNode root) { roots.add(root); } public static AlignmentForest constructFromSteinerTree( DirectedWeightedMultigraph<Node, Link> tree, Node treeRoot, List<HNode> sortedHeaders) { AlignmentForest forest = new AlignmentForest(); // Recursively add the vertices to the forest TNode root = forest.populateWithVertex(treeRoot, tree, null); forest.addRoot(root); forest.reorderTreeNodes(sortedHeaders); /*** Get the final order of the columns from the forest ***/ // The same list sortedHeaders is used to communicate the final order List<HNode> finalOrder = forest.getFinalColumnOrder(sortedHeaders); // Need to preserve the unaligned columns ArrayList<HNode> unalignedColumns = new ArrayList<HNode>(); unalignedColumns.addAll(sortedHeaders); unalignedColumns.removeAll(finalOrder); // Add the aligned columns sortedHeaders.clear(); sortedHeaders.addAll(finalOrder); // Add the unaligned columns sortedHeaders.addAll(unalignedColumns); return forest; } private List<HNode> getFinalColumnOrder(List<HNode> sortedHeaders) { ArrayList<HNode> finalOrder = new ArrayList<HNode>(); Collection<TNode> columnTnodes = TNodeToHNodeMap.keySet(); addToFinalOrderFromChildren(finalOrder, roots, columnTnodes); return finalOrder; } private void addToFinalOrderFromChildren(ArrayList<HNode> finalOrder, List<TNode> nodes, Collection<TNode> columnTnodes) { for (TNode node : nodes) { AlignmentNode alNode = (AlignmentNode) node; if (columnTnodes.contains(node)) finalOrder.add(TNodeToHNodeMap.get(node)); if (alNode.hasChildren()) { addToFinalOrderFromChildren(finalOrder, alNode.getChildren(), columnTnodes); } } } private void reorderTreeNodes(List<HNode> sortedHeaders) { // Assign sequential numbers to all TNodes according to column order HashMap<TNode, Integer> nodeIndexMap = new HashMap<TNode, Integer>(); int counter = 0; for (HNode hNode : sortedHeaders) { // logger.info("Checking for HNODE: " + hNode.getColumnName() + " " // + hNode.getId()); String id = hNode.getId(); TNode node = getAlignmentNodeWithHNodeId(roots, id); // For the columns that did not have a semantic type defined // For e.g. the ones that do not have data if (node == null) { logger.debug("Alignment node returned null!"); continue; } // Check for the special case where the intermediate node is // attached to a column. We added the same semantic type object to // one of its child if (node.getChildren() != null && node.getChildren().size() != 0) { node = getAlignmentNodeWithHNodeId(node.getChildren(), id); } nodeIndexMap.put(node, counter++); // Store the node in a map as it helps in determining final order TNodeToHNodeMap.put(node, hNode); } logger.debug("Node Map: " + nodeIndexMap); for (TNode node : nodeIndexMap.keySet()) { AlignmentNode nodeAl = (AlignmentNode) node; logger.debug(nodeAl.getType().getType() + " of " + nodeAl.getType().getDomain() + " : " + nodeIndexMap.get(node)); } // For each root, sort at each level for (TNode root : roots) { // Collect nodes at each depth HashMap<Integer, ArrayList<TNode>> depthMap = new HashMap<Integer, ArrayList<TNode>>(); calculateDepth(root, 0, depthMap); // Calculate the max depth Set<Integer> depths = depthMap.keySet(); Integer maxDepth = Collections.max(depths); // Sort the nodes at each level starting from second last to top int startingLevel = maxDepth - 1; for (int i = startingLevel; i >= 0; i--) { List<TNode> nodeList = depthMap.get(i); for (TNode node : nodeList) { AlignmentNode nodeAl = (AlignmentNode) node; if (nodeAl.hasChildren()) { int minColIndex = sortChildren(nodeAl, nodeIndexMap); nodeIndexMap.put(nodeAl, minColIndex); } } } } } private int sortChildren(AlignmentNode parent, HashMap<TNode, Integer> nodeIndexMap) { List<TNode> children = parent.getChildren(); logger.debug("Parent: " + parent.getId()); // Case of 1 child if (children.size() == 1) { return nodeIndexMap.get(children.get(0)); } logger.debug("Initial Order:"); for (int i = 0; i < children.size(); i++) { logger.debug(children.get(i).getId() + " "); } int minSeqIndex = Integer.MAX_VALUE; for (int i = 1; i < children.size(); i++) { TNode child = children.get(i); int seqIndex = nodeIndexMap.get(child); logger.debug("Working on " + child.getId() + " Index:" + seqIndex); if (seqIndex < minSeqIndex) minSeqIndex = seqIndex; for (int j = i - 1; j >= 0; j--) { TNode currChild = children.get(j); int currSeqIndex = nodeIndexMap.get(currChild); logger.debug("Current Child: " + currChild.getId() + " Index: " + currSeqIndex); if (currSeqIndex > seqIndex) { logger.debug("SWAPPING!!!" + child.getId() + " with " + currChild.getId()); Collections.swap(children, children.indexOf(child), j); } logger.debug("Order:"); for (int z = 0; z < children.size(); z++) { logger.debug(children.get(z).getId() + " "); } } } logger.debug("Final Order:"); for (int i = 0; i < children.size(); i++) { logger.debug(children.get(i).getId() + " "); } logger.debug("Min Index: " + minSeqIndex); return minSeqIndex; } private void calculateDepth(TNode node, int depth, HashMap<Integer, ArrayList<TNode>> depthMap) { // Populate the node in depth map ArrayList<TNode> nodeList = depthMap.get(depth); if (nodeList == null) { nodeList = new ArrayList<TNode>(); depthMap.put(depth, nodeList); } nodeList.add(node); // Recurse to children AlignmentNode nodeAl = (AlignmentNode) node; if (nodeAl.hasChildren()) { for (TNode child : nodeAl.getChildren()) { calculateDepth(child, depth + 1, depthMap); } } } private TNode getAlignmentNodeWithHNodeId(List<TNode> nodes, String hNodeId) { for (TNode node : nodes) { AlignmentNode alNode = (AlignmentNode) node; if (alNode.hasSemanticType()) { logger.debug("Cheking on sem type: " + alNode.getType().getType() + " ID: " + alNode.getSemanticTypeHNodeId()); if (alNode.getSemanticTypeHNodeId().equals(hNodeId)) { return alNode; } } if (alNode.hasChildren()) { TNode nodeC = getAlignmentNodeWithHNodeId(alNode.getChildren(), hNodeId); if (nodeC != null) return nodeC; } } return null; } ////////NOT WORKING WITH NEW API OF ALIGNMENT //////// SO COMMENTING OUT private TNode populateWithVertex(Node vertex, DirectedWeightedMultigraph<Node, Link> tree, Link parentEdge) { // Add the information about the parent link // AlignmentLink parentLink = null; // if (parentEdge != null) { // parentLink = new AlignmentLink(parentEdge.getId(), // parentEdge.getLabel().getUri()); // } // // // Add the children // List<TNode> children = new ArrayList<TNode>(); // Check if the vertex is an intermediate node that should be attached // to a column. In such case add a blank child node // if (vertex.getSemanticType() != null // && tree.outgoingEdgesOf(vertex).size() != 0) { // logger.debug("Intermediate Node with column attached to it: " // + vertex.getLabel().getUri()); // AlignmentLink link = new AlignmentLink(vertex.getSemanticType() // .getHNodeId() + "BlankLink", "BlankNode"); // TNode node = new AlignmentNode(vertex.getId() + "BlankNodeId", // null, link, "BlankNode", vertex.getSemanticType()); // logger.debug("Created blank node: " + node.getId()); // children.add(node); // } // // Set<Link> edges = tree.outgoingEdgesOf(vertex); // for (Link edge : edges) { // TNode child = populateWithVertex(edge.getTarget(), tree, edge); // children.add(child); // } // AlignmentNode node = new AlignmentNode(vertex.getId(), children, // parentLink, vertex.getLabel().getUri(), vertex.getSemanticType()); // logger.debug("Created node: " + node.getId()); // return node; return null; } }