/* * Copyright 2003-2010 Tufts University Licensed under the * Educational Community 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.osedu.org/licenses/ECL-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. */ /* * HierarchyViewHierarchyModel.java * * Created on December 20, 2003, 11:44 PM */ package tufts.oki.hierarchy; import java.util.*; import tufts.vue.LWComponent; import tufts.vue.LWContainer; import tufts.vue.LWNode; import tufts.vue.LWLink; import tufts.vue.LWHierarchyMap; /** * * @author Daisuke Fujiwara */ public class HierarchyViewHierarchyModel extends HierarchyModel { //hash map which stores information of the hierarchy map data //node hashtable serves the purpose of mapping duplicates to the original nodes private HashMap hierarchyHash; private Hashtable nodeHash; //an arraylist which holds all the nodes that were connected from the root node private ArrayList originalNodes; private LWHierarchyMap hierarchyMap = null; //problems: //fix layout (where to call) //duplicate problem /** Creates a new instance of HierarchyViewHierarchyModel */ public HierarchyViewHierarchyModel(LWNode node, LWHierarchyMap hierarchyMap) { super(); hierarchyHash = new HashMap(); nodeHash = new Hashtable(); originalNodes = new ArrayList(); this.hierarchyMap = hierarchyMap; computeShortestPath(node); createLinks(); layout(getDuplicatedNode(node), null); setUpHierarchyNodes(getDuplicatedNode(node), null); } public HierarchyViewHierarchyModel(LWNode node, LWHierarchyMap hierarchyMap, String name, String description) { super(name, description); hierarchyHash = new HashMap(); nodeHash = new Hashtable(); originalNodes = new ArrayList(); this.hierarchyMap = hierarchyMap; computeShortestPath(node); System.out.println("beginning of links"); createLinks(); layout(getDuplicatedNode(node), null); System.out.println("end of links"); setUpHierarchyNodes(getDuplicatedNode(node), null); } /*** create the duplicates of the nodes and map them to the original nodes using the node hashtable */ public LWNode duplicateNode(LWNode node, LWContainer parent) { LWNode copy = (LWNode)node.duplicate(); //copy.setParent(parent); /* for (Iterator i = node.getNodeIterator(); i.hasNext();) { LWNode childCopy = duplicateNode((LWNode)i.next(), copy); copy.addChild(childCopy); } */ nodeHash.put(node, copy); return copy; } public LWNode getDuplicatedNode(LWNode node) { return (LWNode)nodeHash.get(node); } /**Dijkstra's theorem is used here to compute the shortest path between nodes*/ public void computeShortestPath(LWNode rootNode) { //a vector used for the Dijkstra's theorem Vector nodesVector = new Vector(); //initial set up for the computation for the shortest path nodesVector.add(rootNode); originalNodes.add(rootNode); duplicateNode(rootNode, hierarchyMap); //stores default values to the hierarchy hashmap hierarchyHash.put((LWNode)nodeHash.get(rootNode), new ShortestPathData(null, 0)); //Dijkstra's theorem (shortest path) while(!nodesVector.isEmpty()) { //removes the first element in the vector as it is a queue LWNode currentNode = (LWNode)nodesVector.remove(0); LWNode currentNodeCopy = (LWNode)nodeHash.get(currentNode); //retrieves the current shortest distance to get to the given node from the root node int totalDistance = ((ShortestPathData)hierarchyHash.get(currentNodeCopy)).getDistance(); //iterates through nodes that are connected to the given node for (Iterator i = currentNode.getLinks().iterator(); i.hasNext();) { LWLink connectedLink = (LWLink)i.next(); LWNode nextNode = null; //calculates the distance to adjacent nodes from the root node passing through the given node //could lose the precision.. // todo: how to handle curved links? int length = (int)connectedLink.getPoint1().distance(connectedLink.getPoint2()); totalDistance += length; //gets the component associated with the given link if ((nextNode = (LWNode)connectedLink.getComponent1()) == currentNode) nextNode = (LWNode)connectedLink.getComponent2(); //keep track of nodes that are connected if(!originalNodes.contains(nextNode)) { originalNodes.add(nextNode); duplicateNode(nextNode, hierarchyMap); } LWNode nextNodeCopy = (LWNode)nodeHash.get(nextNode); //if it is the first time traversing through this node or if the calculated distance is shorter //than the shortest distance associated with the adjacent node if (!hierarchyHash.containsKey(nextNodeCopy) || totalDistance < ((ShortestPathData)hierarchyHash.get(nextNodeCopy)).getDistance()) { //updates the distance and parent hashtables and adds to the vector nodesVector.add(nextNode); hierarchyHash.put(nextNodeCopy, new ShortestPathData(currentNodeCopy, totalDistance)); } } } //clears the arraylist contents originalNodes.clear(); /* for (Iterator ii = hierarchyHash.keySet().iterator(); ii.hasNext();) { LWNode a = (LWNode)ii.next(); System.out.println("key " + a.toString()); if(((HierarchyData)hierarchyHash.get(a)).getParent() != null) System.out.println("value " + ((LWNode)((HierarchyData)hierarchyHash.get(a)).getParent()).toString() + "\n\n"); else System.out.println("value null" + "\n\n"); } **/ } /**creates the links for the hierarchy map according to the shortest path*/ public void createLinks() { //verify a path between nodes and create a link between the nodes for (Iterator i = nodeHash.values().iterator(); i.hasNext();) { LWNode child = (LWNode)i.next(); LWNode parent = ((ShortestPathData)hierarchyHash.get(child)).getParent(); //if the node has a parent, then create a link if (parent != null) { LWLink link = new LWLink(parent, child); //how about the link label? } } } /**organizes the nodes in a hierarchy in a recursive fashion*/ public void layout(LWNode currentNode, LWNode previousNode) { //System.out.println("laying out: " + currentNode.toString()) ; LWNode parentNode = ((ShortestPathData)hierarchyHash.get(currentNode)).getParent(); //x and y values which specify the new location of the current node //they are initialized to the root node position (default) float x = 200f, y = 0; if (previousNode != null) { x = previousNode.getX() + previousNode.getLocalBorderWidth() + 2; y = previousNode.getY(); } //if there is no previous node and the parent node exists else if(parentNode != null) { //determines the farthest left node's x location int xRange = 200; x = parentNode.getX() - (xRange / 2); y = parentNode.getY() + 60; } currentNode.setLocation(x, y); //children from here LWNode node = null; //must come up with another algorithm if left to right has some meaning for (Iterator i = currentNode.getLinks().iterator(); i.hasNext();) { //links to nodes LWLink link = (LWLink)i.next(); LWNode nextNode = null; if ((nextNode = (LWNode)link.getComponent1()) == currentNode) nextNode = (LWNode)link.getComponent2(); if(!nextNode.equals(parentNode)) { layout(nextNode, node); node = nextNode; } } //return currentNode; } //only adds LWNode public void setUpHierarchyNodes(LWNode node, HierarchyNode parentNode) { try { HierarchyNode hierarchyNode; originalNodes.add(node); //if a node to be created is a root node if (parentNode == null) { String label, description; if ((label = node.getLabel()) == null) label = new String("Node:" + node.getID()); if ((description = node.getNotes()) == null) description = new String("No description for " + label); hierarchyNode = (HierarchyNode)createRootNode(new tufts.oki.shared.Id(getNextID()), new tufts.oki.shared.VueType(), label, description); hierarchyNode.setLWComponent(node); } //if it is a non root node else hierarchyNode = createHierarchyNode(parentNode, node); //do it recursively for (Iterator i = node.getLinks().iterator(); i.hasNext();) { LWLink link = (LWLink)i.next(); LWComponent nextNode = null; if (link.getComponent1() != node) nextNode = link.getComponent1(); else if (link.getComponent2() != node) nextNode = link.getComponent2(); if(nextNode != null && nextNode instanceof LWNode && !originalNodes.contains(nextNode)) setUpHierarchyNodes((LWNode)nextNode, hierarchyNode); } } catch (osid.hierarchy.HierarchyException he) { System.out.println("hierarchy exception"); } catch (osid.shared.SharedException se) { System.out.println("shared exception"); } } /**A method that creates a hierarch node with a given parent and the given LWComponent*/ private HierarchyNode createHierarchyNode(HierarchyNode parentNode, LWComponent component) { HierarchyNode node = null; try { String label, description; //if there is no label associated with the given component if ((label = component.getLabel()) == null) { if (component instanceof LWLink) label = new String("Link:" + component.getID()); else if (component instanceof LWNode) label = new String("Node:" + component.getID()); } if ((description = component.getNotes()) == null) description = new String("No description for " + label); //creates a hierarchy node and sets its LWcomponent to the given one node = (HierarchyNode)createNode(new tufts.oki.shared.Id(getNextID()), parentNode.getId(), new tufts.oki.shared.VueType(), label, description); node.setLWComponent(component); } catch (osid.hierarchy.HierarchyException he) { System.err.println("exception creating a node"); } catch (osid.shared.SharedException se) { System.err.println("exception creating a node from shared"); } catch (Exception e) { //possible null pointer System.err.println(this + " createHierarchyNode " + e); e.printStackTrace(); } return node; } /**A method that deletes the given node*/ private void deleteHierarchyNode(HierarchyNode parentNode, HierarchyNode childNode) { try { deleteNode(childNode.getId()); //reloadTreeModel(parentNode); } catch(osid.hierarchy.HierarchyException he) { System.out.println("deleting node bug"); } catch(Exception e) { System.err.println(this + " deleteHierarchyNode " + e); e.printStackTrace(); } } /**A class which stores the information of the hierarchy*/ private class ShortestPathData { private LWNode parentNode; private int distance; public ShortestPathData(LWNode parentNode, int distance) { this.parentNode = parentNode; this.distance = distance; } //gets the distance associated with the given node public int getDistance() { return distance; } //gets the parent node associated with the given node public LWNode getParent() { return parentNode; } } }