/******************************************************************************** * * * (c) Copyright 2010 Verizon Communications USA and The Open University UK * * * * This software is freely distributed in accordance with * * the GNU Lesser General Public (LGPL) license, version 3 or later * * as published by the Free Software Foundation. * * For details see LGPL: http://www.fsf.org/licensing/licenses/lgpl.html * * and GPL: http://www.fsf.org/licensing/licenses/gpl-3.0.html * * * * This software is provided by the copyright holders and contributors "as is" * * and any express or implied warranties, including, but not limited to, the * * implied warranties of merchantability and fitness for a particular purpose * * are disclaimed. In no event shall the copyright owner or contributors be * * liable for any direct, indirect, incidental, special, exemplary, or * * consequential damages (including, but not limited to, procurement of * * substitute goods or services; loss of use, data, or profits; or business * * interruption) however caused and on any theory of liability, whether in * * contract, strict liability, or tort (including negligence or otherwise) * * arising in any way out of the use of this software, even if advised of the * * possibility of such damage. * * * ********************************************************************************/ package com.compendium.ui; import java.awt.Point; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.compendium.LanguageProperties; import com.compendium.ProjectCompendium; import com.compendium.core.datamodel.IModel; import com.compendium.core.datamodel.Link; import com.compendium.core.datamodel.NodePosition; import com.compendium.core.datamodel.NodeSummary; import com.compendium.core.datamodel.PCSession; import com.compendium.core.datamodel.View; import com.compendium.core.datamodel.services.IViewService; /** * UIArrange defines code to arrange a map of nodes tidily * * @author Sajid Ali / Cheralathan Balakrishnan / Michelle Bachler / Alex Jarocha-Ernst */ public class UIArrange implements IUIConstants { /** * class's own logger */ final Logger log = LoggerFactory.getLogger(getClass()); /** The vertical distance between nodes when arranged.*/ private final int VERT_SEP = 10; /** The horizontal distance between .*/ private final int HORIZ_SEP = 30; /** The nodes to arrange.*/ private Hashtable htNodes = new Hashtable(51); /** The nodes against their levels.*/ private Hashtable htNodesLevel = new Hashtable(51); /** The nodes and their children.*/ private Hashtable htNodesBelow = new Hashtable(51); /** The nodes against their ancestry.*/ private Hashtable htNodesAbove = new Hashtable(); /** Used for undo operations.*/ private Hashtable nodePositionsCloneHashtable = new Hashtable(); /** Used for redo operations.*/ private Hashtable nodePositionsCloneHashtableForRedo = new Hashtable(); /** Nodes at level one.*/ private Vector vtLevelOneNodes = new Vector(51); /** Links to arrange.*/ private Vector vtLinks = new Vector(51); /** Nodes against their levels.*/ private Vector vtNodesLevel = new Vector(51); /** Nodes against thier levels in order.*/ private Vector vtNodesLevelOrdered = new Vector(51); /** List of node levels.*/ private Vector nodeLevelList = new Vector(); /** The view service for accessing the databse.*/ private IViewService vs = null; /** The session object for the current user with the current database.*/ private PCSession session = null; /** The maximum indent level.*/ private int nMaxLevel = 0; /** * Constructor. Does nothng. */ public UIArrange() {} /** * Clear all the Vectors and Hashtables used to perform the arrange operations. */ public void clearData() { htNodesLevel.clear(); htNodes.clear(); htNodesBelow.clear(); htNodesAbove.clear(); vtLinks.removeAllElements(); vtLevelOneNodes.removeAllElements(); vtNodesLevel.removeAllElements(); vtNodesLevelOrdered.removeAllElements(); nodeLevelList.removeAllElements(); } /** * Process the given view for arranging. * @param view com.compendium.core.datamodel.View, the view to arrange. */ public boolean processView(View view) { clearData(); IModel model = ProjectCompendium.APP.getModel(); session = model.getSession(); vs = model.getViewService(); Vector vtTemp = new Vector(); try { vtTemp = vs.getNodePositions(session, view.getId()); } catch(Exception ex) { ProjectCompendium.APP.displayError(LanguageProperties.getString(LanguageProperties.UI_GENERAL_BUNDLE, "UIArrange.cannotGetNodes") + view.getLabel()+"." + ex.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ } nodePositionsCloneHashtable.clear(); for ( int i = 0; i < vtTemp.size(); i++) { nodePositionsCloneHashtable.put( ((NodePosition)vtTemp.elementAt(i)).getNode().getId(), ((NodePosition)vtTemp.elementAt(i)).getClone()); } // GET NODES FOR THIS VIEW AND STORE INTO HASTABLE BY ID for(Enumeration en = vtTemp.elements();en.hasMoreElements();) { NodePosition nodePos = (NodePosition)en.nextElement(); NodeSummary node = nodePos.getNode(); String key = node.getId(); htNodes.put(key, node); } for(Enumeration f = htNodes.elements();f.hasMoreElements();) { NodeSummary nodeToPrint = (NodeSummary)f.nextElement(); //store the node into the hashtable.. the key being the nodeid, the object being the level integer String key = nodeToPrint.getId(); htNodesLevel.put(key,new Integer(0)); } //get the nodes and level numbers into a ht htNodesLevel if (!startLevelCalculation(view)) return false; return true; } /** * This will arrange the nodes in the given view. * @param view com.compendium.core.datamodel.View, the view to arrange. * @param viewFrame com.compenduim.ui.UIViewFrame, the frame of the map view to arrange. */ public void arrangeView(View view, UIViewFrame viewFrame) { UIViewPane uiViewPane = null; if (viewFrame instanceof UIMapViewFrame) uiViewPane = ((UIMapViewFrame)viewFrame).getViewPane(); processView(view); //get the number of levels int highestLevel = 0; for(Enumeration e = htNodesLevel.keys();e.hasMoreElements();) { String nodeId = (String)e.nextElement(); int level = ((Integer)htNodesLevel.get(nodeId)).intValue(); String label = ((NodeSummary)htNodes.get(nodeId)).getLabel(); if (level > highestLevel) { highestLevel = level; } } int[] horizontalSep = new int[highestLevel]; int leafcount = 0; int levelonecount = 0; int maxWidth = 0; int maxLevel = 0; //get the maxiumum width of a uinode based on its bounds for(Enumeration e = htNodesLevel.keys();e.hasMoreElements();) { String nodeId = (String)e.nextElement(); //get the uinode and get its bounds/ccordinates UINode uinode = ((UINode) ((UIMapViewFrame)viewFrame).getViewPane().get(nodeId)); int level = ((Integer)htNodesLevel.get(nodeId)).intValue(); if (level > maxLevel) { maxLevel = level; } // ALLOW FOR FACT VIEW MIGHT BE SCALED int width = uinode.getWidth(); if (uiViewPane != null) { Point p = UIUtilities.scalePoint(width, width, uiViewPane.getScale()); width = p.x; } if (level > 0) { int sepLevel = horizontalSep[level-1]; if(width > sepLevel) horizontalSep[level-1] = width + HORIZ_SEP; } } setYPositionForNodes(viewFrame); sortNodesParentwise(); compactNodesInList(viewFrame, (Vector)nodeLevelList.elementAt(0)); //this hashtable maintains a count of nodes present on a perticular level (key) Hashtable htCount = new Hashtable(); for(int i=vtNodesLevel.size()-1; i >= 0; i--) { String nodeId = (String)vtNodesLevel.elementAt(i); int level = ((Integer)htNodesLevel.get(nodeId)).intValue(); int countAtLevel = 0; int bufferToCenter = 0; int numberOfChildren = 0; if(htNodesBelow.containsKey(nodeId)) numberOfChildren = ((Vector)htNodesBelow.get(nodeId)).size(); if(htCount.containsKey(new Integer(level))) countAtLevel = ((Integer)htCount.get(new Integer(level))).intValue(); countAtLevel++; if(numberOfChildren > 0) countAtLevel = countAtLevel + (new Integer((numberOfChildren+1)/2)).intValue(); htCount.put(new Integer(level), new Integer(countAtLevel)); //get the uinode and set its bounds/ccordinates UINode uinode = ((UINode) ((UIMapViewFrame)viewFrame).getViewPane().get(nodeId)); if(uinode != null) { //get the x and y coordinates with the text field width in view for right centering.. int newX = 0; if (view.equals(ProjectCompendium.APP.getHomeView())) { newX = 70; } int j = 0; for (j = 0; j < level; j++) { newX += horizontalSep[j]; } // ALLOW FOR FACT VIEW MIGHT BE SCALED int width = uinode.getWidth(); if (uiViewPane != null) { Point p = UIUtilities.scalePoint(width, width, uiViewPane.getScale()); width = p.x; } newX -= ((horizontalSep[j-1]/2) + (width/2)); int newY = uinode.getNodePosition().getYPos(); Point ptNew = new Point(newX,newY); //uinode.setPosition(ptNew); Point loc = UIUtilities.transformPoint(newX, newY, uiViewPane.getScale()); uinode.setBounds(loc.x, loc.y, uinode.getWidth(), uinode.getHeight()); uinode.updateLinks(); try { view.setNodePosition(nodeId, ptNew); } catch(Exception ex) { log.error("Error...", ex); log.info("Error: (UIArrange.arrangeView) \n\n"+ex.getMessage()); //$NON-NLS-1$ } } } if (uiViewPane != null) uiViewPane.repaint(); Vector vtTemp = new Vector(); try { vtTemp = vs.getNodePositions(session, view.getId()); } catch(Exception ex) { ProjectCompendium.APP.displayError(LanguageProperties.getString(LanguageProperties.UI_GENERAL_BUNDLE, "UIArrange.cannotGetNodes") + view.getLabel()+". " + ex.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ } nodePositionsCloneHashtableForRedo.clear(); for ( int i = 0; i < vtTemp.size(); i++) { nodePositionsCloneHashtableForRedo.put( ((NodePosition)vtTemp.elementAt(i)).getNode().getId(), ((NodePosition)vtTemp.elementAt(i)).getClone()); } } /** * Helper method. Calculate Level One Nodes here. * @param view com.compendium.core.datamodel.View, the view to calculate lvel noe nodes for. */ private int calculateLevelOneNodes(View view) { int nLevel = 1; // upgrade the level with the current view //nLevel = nNodeLevelUp; IModel model = ProjectCompendium.APP.getModel(); PCSession session = ProjectCompendium.APP.getModel().getSession(); IViewService vs = model.getViewService(); vtLinks = new Vector(51); try { vtLinks = vs.getLinks(session, view.getId()); } catch(Exception ex) { ProjectCompendium.APP.displayError("Exception: (UIArrange.calculateLevelOneNodes) " + ex.getMessage()); //$NON-NLS-1$ } if(htNodes.size() == 0) return -1; /** * get each node and check if it is level 1 node */ for(Enumeration e = htNodes.elements();e.hasMoreElements();) { int nNodeLevelOne = 1; NodeSummary node = (NodeSummary)e.nextElement(); String key = node.getId(); // execute only if the node is new if ( ((Integer)htNodesLevel.get(key)).intValue() == 0 ) { /** * Compare one end of the link with the current node for a match * link.getTo() ------------<<------------ link.getFrom() * ^^^^^^^^^^^^^^ * nodeOther = link.getFrom() */ // get links corresponding to this node for (Enumeration eL = vtLinks.elements(); eL.hasMoreElements();) { Link link = (Link)eL.nextElement(); // TO FIX OLD BUG WHICH SHOULD NOT LONGER HAPPEN if ( (link.getFrom().getId()).equals( link.getTo().getId() ) ) { vtLinks.remove((Object)link); } else if( (link.getFrom()).getId().equals(key) || (link.getTo()).getId().equals(key)) { if( (link.getFrom()).getId().equals(node.getId())) { nNodeLevelOne *= 0; break; } else { nNodeLevelOne *= 1; } } } /** * if node on level one then export it. */ if(nNodeLevelOne == 1) { // do for new nodes (whose levels are not set) if(!(node.getLabel()).equals("Home Window")) { //$NON-NLS-1$ // add to vector with level one nodes vtLevelOneNodes.addElement(node); // set the level number for this node htNodesLevel.put(key, new Integer(nNodeLevelOne)); } } //log.info(node.getLabel() + " at level " + nNodeLevelOne); } } // Reversing the elements of levelonenodes vector into a temp vector for proper // ordering.. the elements in the levelonenodes vector seems to be in the reverse order! Vector vtTemp = new Vector(); int numOfNodes = vtLevelOneNodes.size(); vtTemp.setSize(numOfNodes); int pos = numOfNodes-1; for (Enumeration eN = vtLevelOneNodes.elements(); eN.hasMoreElements();) { NodeSummary node = (NodeSummary)eN.nextElement(); vtTemp.setElementAt(node,pos--); } vtLevelOneNodes.removeAllElements(); vtLevelOneNodes = vtTemp; return nLevel; } /** * Helper method. Handle the level calculations, * @param view com.compendium.core.datamodel.View, the view to calculate levels for. */ private boolean startLevelCalculation(View view) { int nLevel = 1; String sNodeInformation = ""; //$NON-NLS-1$ nLevel = calculateLevelOneNodes(view); // Reversing the elements of vtLinks vector into a temp vector for proper // ordering.. the elements in the vtLinks vector seems to be in the reverse order! Vector vtTemp = new Vector(); int numOfNodes = vtLinks.size(); vtTemp.setSize(numOfNodes); int pos = numOfNodes-1; for (Enumeration eN = vtLinks.elements(); eN.hasMoreElements();) { Link link = (Link)eN.nextElement(); vtTemp.setElementAt(link,pos--); } vtLinks.removeAllElements(); vtLinks = vtTemp; /** * For all level one nodes start recursive export */ for(int i=0;i<vtLevelOneNodes.size();i++) { //set the node in the position vector NodeSummary levelOneNode = ((NodeSummary)vtLevelOneNodes.elementAt(i)); vtNodesLevel.addElement(levelOneNode.getId()); int yPositionForLevel1 = ( (NodePosition)nodePositionsCloneHashtable.get(levelOneNode.getId())).getYPos(); int secondYPositionForLevel1 = 0; int indexForLevel1 = 0; while (nodeLevelList.size() == 0) { nodeLevelList.addElement(new Vector()); } Vector nodeListAtLevel1 = (Vector)nodeLevelList.elementAt(0); secondYPositionForLevel1 = 0; // SOME SORT OF NODE ORFERING BY LEVEL AND Y POSITION ? indexForLevel1 = 0; while ((indexForLevel1 < nodeListAtLevel1.size()) && (yPositionForLevel1 > secondYPositionForLevel1)) { secondYPositionForLevel1 = ( (NodePosition)nodePositionsCloneHashtable.get(nodeListAtLevel1.elementAt(indexForLevel1))).getYPos(); indexForLevel1++; } if (indexForLevel1 == nodeListAtLevel1.size()) { if (yPositionForLevel1 > secondYPositionForLevel1) nodeListAtLevel1.addElement(levelOneNode.getId()); else nodeListAtLevel1.insertElementAt(levelOneNode.getId(), (indexForLevel1 == 0)?indexForLevel1:indexForLevel1 - 1); } else { nodeListAtLevel1.insertElementAt(levelOneNode.getId(), (indexForLevel1 == 0)?indexForLevel1:indexForLevel1 - 1); } boolean bCheckNode = false; int debugCount = 0; // PROCESS CURRENT NODES LINKS for(Enumeration e = vtLinks.elements(); e.hasMoreElements();) { Link link = (Link)e.nextElement(); // check if this link is tied to the given node.. if((link.getTo()).getId().equals(levelOneNode.getId())) { // get the from node from this link NodeSummary node = (NodeSummary)htNodes.get((link.getFrom()).getId()); Integer previousLevel = (Integer)htNodesLevel.get(node.getId()); if ( (previousLevel != null) && (previousLevel.intValue() < (nLevel+1))) { htNodesLevel.put(node.getId(),new Integer(nLevel+1)); } //increment the vector of nodes below this node Vector oldVector = null; if(htNodesBelow.get(levelOneNode.getId()) != null) oldVector = (Vector)htNodesBelow.get(levelOneNode.getId()); else oldVector = new Vector(); int yPosition = ( (NodePosition)nodePositionsCloneHashtable.get(node.getId())).getYPos(); int secondYPosition = 0; int index = 0; boolean found = false; while ((index < oldVector.size()) && (yPosition > secondYPosition)) { secondYPosition = ( (NodePosition)nodePositionsCloneHashtable.get(oldVector.elementAt(index))).getYPos(); if (node.getId().equals((String)oldVector.elementAt(index))) { found = true; break; } index++; } if (index == oldVector.size()) { if (yPosition > secondYPosition) { oldVector.addElement(node.getId()); } else if (!found) { oldVector.insertElementAt(node.getId(), (index == 0)?index:index - 1); } } else if (!found) { oldVector.insertElementAt(node.getId(), (index == 0)?index:index - 1); } NodeSummary mainNode = (NodeSummary)htNodes.get(node.getId()); NodeSummary parentNode = (NodeSummary)htNodes.get(levelOneNode.getId()); mainNode.setParentNode(parentNode); //add the parent if (htNodesAbove.get(node.getId()) != null) { String nodeAboveId = (String)htNodesAbove.get(node.getId()); NodeSummary nodeAbove = (NodeSummary)htNodes.get(nodeAboveId); NodePosition np = ( (NodePosition)nodePositionsCloneHashtable.get(nodeAboveId)); int previousParentYPosition = np.getYPos(); int currentParentYPosition = ( (NodePosition)nodePositionsCloneHashtable.get(levelOneNode.getId())).getYPos(); if (currentParentYPosition > previousParentYPosition) { htNodesAbove.put(node.getId(), levelOneNode.getId()); } } else { htNodesAbove.put(node.getId(), levelOneNode.getId()); } if ((previousLevel != null) && (previousLevel.intValue() != 0)) { if (previousLevel.intValue() < (nLevel + 1)) { ((Vector)nodeLevelList.elementAt(previousLevel.intValue()-1)).removeElement(node.getId()); } else { //oldVector.addElement(node.getId()); htNodesBelow.put(levelOneNode.getId(),oldVector); //set the node in the position vector vtNodesLevel.addElement(node.getId()); if (!startRecursiveCalculations(node,(nLevel+1))) return false; continue; } } while (nodeLevelList.size() < (nLevel + 1)) { nodeLevelList.addElement(new Vector()); } Vector nodeListAtLevel = (Vector)nodeLevelList.elementAt(nLevel); secondYPosition = 0; index = 0; while ((index < nodeListAtLevel.size()) && (yPosition > secondYPosition)) { secondYPosition = ( (NodePosition)nodePositionsCloneHashtable.get(nodeListAtLevel.elementAt(index))).getYPos(); index++; } if (index == nodeListAtLevel.size()) { if (yPosition > secondYPosition) { nodeListAtLevel.addElement(node.getId()); } else { nodeListAtLevel.insertElementAt(node.getId(), (index == 0)?index:index - 1); } } else { nodeListAtLevel.insertElementAt(node.getId(), (index == 0)?index:index - 1); } //oldVector.addElement(node.getId()); htNodesBelow.put(levelOneNode.getId(),oldVector); //set the node in the position vector vtNodesLevel.addElement(node.getId()); if (!startRecursiveCalculations(node,(nLevel+1))) return false; } } } return true; } /** * Helper method. This will calculate recursively all the nodes below. the given node. * @param node com.compendium.core.datamodel.NodeSummary, the node to calculate for. * @param LN, the level number. */ private boolean startRecursiveCalculations(NodeSummary node, int LN) { boolean bCheckNode = false; NodeSummary nodeFrom = new NodeSummary(); String sToNodeID = node.getId(); // get the links to this node for(Enumeration e = vtLinks.elements(); e.hasMoreElements();) { int levelNumber = LN; Link link = (Link)e.nextElement(); // check if this link is tied to the given node.. if((link.getTo()).getId().equals(sToNodeID)) { if (link.getTo().getId().equals(link.getFrom().getId()) ) { ProjectCompendium.APP.displayError(LanguageProperties.getString(LanguageProperties.UI_GENERAL_BUNDLE, "UIArrange.message1a")+ //$NON-NLS-1$ ": '"+node.getLabel()+"' "+ //$NON-NLS-1$ //$NON-NLS-2$ LanguageProperties.getString(LanguageProperties.UI_GENERAL_BUNDLE, "UIArrange.message1b")+"\n\n"+ //$NON-NLS-1$ //$NON-NLS-2$ LanguageProperties.getString(LanguageProperties.UI_GENERAL_BUNDLE, "UIArrange.message1c")); //$NON-NLS-1$ //$NON-NLS-2$ return false; } // get the from node from this link nodeFrom = (NodeSummary)htNodes.get((link.getFrom()).getId()); if (nodeFrom == null) { continue; } levelNumber += 1; String sFromNodeID = nodeFrom.getId(); // store the node in nodelevel hashtable with level info Integer previousLevel = (Integer)htNodesLevel.get(sFromNodeID); if ( (previousLevel != null) && (previousLevel.intValue() < levelNumber)) { htNodesLevel.put(sFromNodeID, new Integer(levelNumber)); //if (!htNodesLinks.containsKey(sFromNodeID)) // htNodesLinks.put(sFromNodeID, new Hashtable()); //Hashtable htLinks = htNodes.get(sFromNodeID); //htLinks.put(levelNumber, link); //htNodesLinks.put(sFromNodeID, htLinks) } //increment the vector of nodes below this node Vector oldVector = null; if(htNodesBelow.get(node.getId()) != null) oldVector = (Vector)htNodesBelow.get(node.getId()); else oldVector = new Vector(); int yPosition = ( (NodePosition)nodePositionsCloneHashtable.get(sFromNodeID)).getYPos(); int secondYPosition = 0; int index = 0; boolean found = false; while ((index < oldVector.size()) && (yPosition > secondYPosition)) { //NodeSummary tempNode = (NodeSummary)htNodes.get(oldVector.elementAt(index)); secondYPosition = ( (NodePosition)nodePositionsCloneHashtable.get(oldVector.elementAt(index))).getYPos(); if (nodeFrom.getId().equals((String)oldVector.elementAt(index))) { found = true; break; } index++; } if (index == oldVector.size()) { if (yPosition > secondYPosition) { oldVector.addElement(nodeFrom.getId()); } else if (!found) { oldVector.insertElementAt(nodeFrom.getId(), (index == 0)?index:index - 1); } } else if (!found) { oldVector.insertElementAt(nodeFrom.getId(), (index == 0)?index:index - 1); } NodeSummary oFromNode = (NodeSummary)htNodes.get(sFromNodeID); NodeSummary parentNode = (NodeSummary)htNodes.get(sToNodeID); oFromNode.setParentNode(parentNode); //add the parent if (htNodesAbove.get(nodeFrom.getId()) != null) { int previousParentYPosition = ( (NodePosition)nodePositionsCloneHashtable.get((String)htNodesAbove.get(sFromNodeID))).getYPos(); int currentParentYPosition = ( (NodePosition)nodePositionsCloneHashtable.get(sToNodeID)).getYPos(); if (currentParentYPosition > previousParentYPosition) { htNodesAbove.put(sFromNodeID, sToNodeID); } } else { htNodesAbove.put(sFromNodeID, sToNodeID); } if ((previousLevel != null) && (previousLevel.intValue() != 0)) { if (previousLevel.intValue() < levelNumber) { ((Vector)nodeLevelList.elementAt(previousLevel.intValue()-1)).removeElement(sFromNodeID); } else { htNodesBelow.put(sToNodeID, oldVector); //the maximum level number if(levelNumber > nMaxLevel) nMaxLevel = levelNumber; //set the node in the position vector vtNodesLevel.addElement(sFromNodeID); if (!startRecursiveCalculations(nodeFrom, levelNumber)) return false; continue; } } while (nodeLevelList.size() < levelNumber) { nodeLevelList.addElement(new Vector()); } Vector nodeListAtLevel = (Vector)nodeLevelList.elementAt(levelNumber - 1); secondYPosition = 0; index = 0; while ((index < nodeListAtLevel.size()) && (yPosition > secondYPosition)) { secondYPosition = ( (NodePosition)nodePositionsCloneHashtable.get(nodeListAtLevel.elementAt(index))).getYPos(); index++; } if (index == nodeListAtLevel.size() ) { if (yPosition > secondYPosition) { nodeListAtLevel.addElement(sFromNodeID); } else { nodeListAtLevel.insertElementAt(sFromNodeID, (index == 0)?index:index - 1); } } else { nodeListAtLevel.insertElementAt(sFromNodeID, (index == 0)?index:index - 1); } htNodesBelow.put(sToNodeID, oldVector); //the maximum level number if(levelNumber > nMaxLevel) nMaxLevel = levelNumber; //set the node in the position vector vtNodesLevel.addElement(sFromNodeID); if (!startRecursiveCalculations(nodeFrom, levelNumber)) return false; } } return true; } /** * Helper method. Sort the node calculated so far by thier parents. */ private void sortNodesParentwise() { for (int i = 1 ; i < nodeLevelList.size(); i++) { Vector currentLevelList = (Vector)nodeLevelList.elementAt(i); for (int j = 0; j < currentLevelList.size(); j++) { int firstParentYPosition = ((NodePosition)nodePositionsCloneHashtable.get( ((NodeSummary)htNodes.get(currentLevelList.elementAt(j))).getParentNode().getId()) ).getYPos(); for (int k = j + 1; k < currentLevelList.size(); k++) { NodeSummary node = (NodeSummary)htNodes.get(currentLevelList.elementAt(k)); if (node != null) { NodeSummary parent = node.getParentNode(); if (parent != null) { String sID = parent.getId(); NodePosition curNode = (NodePosition)nodePositionsCloneHashtable.get(sID); if (curNode != null) { int secondParentYPosition = curNode.getYPos(); if (firstParentYPosition > secondParentYPosition) { String firstParent = (String)currentLevelList.elementAt(j); currentLevelList.setElementAt(currentLevelList.elementAt(k), j); currentLevelList.setElementAt(firstParent, k); firstParentYPosition = ((NodePosition)nodePositionsCloneHashtable.get( ((NodeSummary)htNodes.get(currentLevelList.elementAt(j))).getParentNode().getId()) ).getYPos(); } } } } } } } } /** * This method is added to enable the child nodes to be positioned at the center relative to the parent. * @param viewFrame com.compendium.ui.UIViewFrame, the frame of the nodes to set the y positions for. */ private void setYPositionForNodes(UIViewFrame viewFrame) { int yPosition = 20; // TO FIX an ArrayIndexOutOfBoundsException if ( nodeLevelList != null && nodeLevelList.size() > 0 ) { if (nodeLevelList.elementAt(0) != null && ((Vector)nodeLevelList.elementAt(0)).size() > 0) { for (int i = 0; i < ((Vector)nodeLevelList.elementAt(0)).size(); i++) { yPosition = calculateYPosition(viewFrame, (String)((Vector)nodeLevelList.elementAt(0)).elementAt(i), yPosition); } } } } /** * Helper Method. This method is added to enable the child nodes to be positioned at the center relative to the parent. * @param viewFrame com.compendium.ui.UIViewFrame, the frame of the node to calculate the y positions for. * @param nodeId, the id of the node to calculate the y position for. * @param yPosition, the starting y position. * @return int, the calculated y position. */ private int calculateYPosition(UIViewFrame viewFrame, String nodeId, int yPosition) { UIViewPane viewPane = ((UIMapViewFrame)viewFrame).getViewPane(); Vector childNodeList = (Vector) htNodesBelow.get(nodeId); UINode parentNode = ((UINode)viewPane.get(nodeId)); if (childNodeList != null) { for (int i = 0; i < childNodeList.size(); i++) { UINode childNode = ((UINode)viewPane.get((String)childNodeList.elementAt(i))); yPosition = calculateYPosition(viewFrame, (String)childNodeList.elementAt(i), yPosition); } int firstChildPosition = ((UINode)viewPane.get((String)childNodeList.firstElement())).getNodePosition().getYPos(); int lastChildPosition = ((UINode)viewPane.get((String)childNodeList.lastElement())).getNodePosition().getYPos(); int newYPosition = firstChildPosition + ((lastChildPosition - firstChildPosition) / 2); int nodeLevel = ((Integer)htNodesLevel.get(nodeId)).intValue(); int indexOfNode = ((Vector)nodeLevelList.elementAt(nodeLevel - 1)).indexOf(nodeId); int indexOfPreviousNode = indexOfNode - 1; int previousNodeYPosition = 0; UINode node = ((UINode)viewPane.get(nodeId)); if (indexOfPreviousNode > -1) { UINode previousNode = ((UINode)viewPane.get((String)((Vector)nodeLevelList.elementAt(nodeLevel-1)).elementAt(indexOfPreviousNode))); previousNodeYPosition = previousNode.getNodePosition().getYPos(); // ALLOW FOR FACT VIEW MIGHT BE SCALED int nodeHeight = previousNode.getHeight(); Point p1 = UIUtilities.scalePoint(nodeHeight, nodeHeight, viewPane.getScale()); nodeHeight = p1.x; if ((previousNodeYPosition + nodeHeight) > newYPosition) { newYPosition = previousNodeYPosition + nodeHeight + (VERT_SEP); } } Point pos = node.getNodePosition().getPos(); node.getNodePosition().setPos(new Point(pos.x, newYPosition)); if (newYPosition > yPosition) { yPosition = newYPosition; } } else { UINode node = ((UINode)viewPane.get(nodeId)); //begin edit, Alex Jarocha-Ernst //previously, this didn't do anything with the previous node of a leaf //this caused problems when the first node on a level had children, but //successive ones did not int nodeLevel = ((Integer)htNodesLevel.get(nodeId)).intValue(); int indexOfNode = ((Vector)nodeLevelList.elementAt(nodeLevel - 1)).indexOf(nodeId); int indexOfPreviousNode = indexOfNode - 1; int previousNodeYPosition = 0; if(indexOfPreviousNode > -1) { UINode previousNode = ((UINode)viewPane.get((String)((Vector)nodeLevelList.elementAt(nodeLevel-1)).elementAt(indexOfPreviousNode))); previousNodeYPosition = previousNode.getNodePosition().getYPos(); // ALLOW FOR FACT VIEW MIGHT BE SCALED int nodeHeight = previousNode.getHeight(); Point p1 = UIUtilities.scalePoint(nodeHeight, nodeHeight, viewPane.getScale()); nodeHeight = p1.x; if ((previousNodeYPosition + nodeHeight) > yPosition) { yPosition = previousNodeYPosition + nodeHeight + (VERT_SEP); } Point pos = node.getNodePosition().getPos(); node.getNodePosition().setPos(new Point(pos.x, yPosition)); } else { //end edit, Alex Jarocha-Ernst Point pos = node.getNodePosition().getPos(); node.getNodePosition().setPos(new Point(pos.x, yPosition)); // ALLOW FOR FACT VIEW MIGHT BE SCALED int nodeHeight = node.getHeight(); Point p1 = UIUtilities.scalePoint(nodeHeight, nodeHeight, viewPane.getScale()); nodeHeight = p1.x; yPosition += (VERT_SEP + nodeHeight); } } return yPosition; } /** * Helper Method. This method compacts the nodes in the given list. * @param viewFrame com.compendium.ui.UIViewFrame, the frame of the node to compact. * @param nodeList, the list of node to compact. */ private void compactNodesInList(UIViewFrame viewFrame, Vector nodeList) { UIViewPane viewPane = ((UIMapViewFrame)viewFrame).getViewPane(); Hashtable compactDoneForNodes = new Hashtable(); for (int i = 0; i < nodeList.size(); i++) { String nodeID = (String)nodeList.elementAt(i); UINode tmpNode = ((UINode)viewPane.get((String)nodeList.elementAt(i))); Vector childNodeList = (Vector)htNodesBelow.get(nodeID); if (childNodeList != null) { compactNodesInList(viewFrame, childNodeList); } } int currentNodeYPosition = 0; int previousNodeYPosition = 0; int compactAmount = 0; for (int i = 1; i < nodeList.size(); i++) { UINode currentNode = ((UINode)viewPane.get((String)nodeList.elementAt(i))); UINode previousNode = ((UINode)viewPane.get((String)nodeList.elementAt(i-1))); currentNodeYPosition = currentNode.getNodePosition().getYPos(); previousNodeYPosition = previousNode.getNodePosition().getYPos(); UINode node = ((UINode)viewPane.get((String)nodeList.elementAt(i))); // ALLOW FOR FACT VIEW MIGHT BE SCALED int nodeHeight = node.getHeight(); Point p1 = UIUtilities.scalePoint(nodeHeight, nodeHeight, viewPane.getScale()); nodeHeight = p1.x; if ((currentNodeYPosition - previousNodeYPosition - nodeHeight) > VERT_SEP) { compactDoneForNodes.clear(); compactAmount = checkCompact(viewFrame, (String)nodeList.elementAt(i), currentNodeYPosition - previousNodeYPosition - VERT_SEP - nodeHeight, compactDoneForNodes); if (compactAmount > 0) { compact(viewFrame, (String)nodeList.elementAt(i), compactAmount, compactDoneForNodes); } } Vector childNodeList = (Vector)htNodesBelow.get((String)nodeList.elementAt(i)); if (childNodeList != null) { UINode firstNode = (UINode)viewPane.get((String)childNodeList.firstElement()); int firstNodeYPosition = firstNode.getNodePosition().getYPos(); UINode lastNode = (UINode)viewPane.get((String)childNodeList.lastElement()); int lastNodeYPosition = lastNode.getNodePosition().getYPos(); previousNodeYPosition = 0; int previousNodeHeight = 0; int level = ((Integer)htNodesLevel.get((String)nodeList.elementAt(i))).intValue(); int indexOfCurrentNode = ((Vector)nodeLevelList.elementAt(level-1)).indexOf((String)nodeList.elementAt(i)); int indexOfPreviousNode = indexOfCurrentNode - 1; if (indexOfPreviousNode > -1) { previousNodeYPosition = previousNode.getNodePosition().getYPos(); // ALLOW FOR FACT VIEW MIGHT BE SCALED previousNodeHeight = previousNode.getHeight(); Point p2 = UIUtilities.scalePoint(previousNodeHeight, previousNodeHeight, viewPane.getScale()); previousNodeHeight = p2.x; Point pos = node.getNodePosition().getPos(); int centerPosition = firstNodeYPosition + (lastNodeYPosition - firstNodeYPosition)/2; if ((previousNodeYPosition + previousNodeHeight + VERT_SEP) < centerPosition) { node.getNodePosition().setPos(new Point(pos.x, centerPosition)); } else { node.getNodePosition().setPos(new Point(pos.x, previousNodeYPosition + previousNodeHeight + VERT_SEP)); } } } } } /** * Helper Method. This method checks the compactness of the node with the given id. * @param viewFrame com.compendium.ui.UIViewFrame, the frame of the node to compact. * @param nodeId, the id of the node to check. * @param compactAmount, the amount to compact by. * @param compactDoneForNodes, nodes already compacted. */ private int checkCompact(UIViewFrame viewFrame, String nodeId, int compactAmount, Hashtable compactDoneForNodes) { UIViewPane viewPane = ((UIMapViewFrame)viewFrame).getViewPane(); compactDoneForNodes.put(nodeId, new Boolean(false)); //get the level of this node UINode currentNode = ((UINode)viewPane.get(nodeId)); int currentNodeYPosition = currentNode.getNodePosition().getYPos(); int level = ((Integer)htNodesLevel.get(nodeId)).intValue(); int indexOfCurrentNode = ((Vector)nodeLevelList.elementAt(level-1)).indexOf(nodeId); int indexOfPreviousNode = indexOfCurrentNode - 1; if (indexOfPreviousNode < 0) { if (currentNodeYPosition < compactAmount) { compactAmount = currentNodeYPosition; } indexOfPreviousNode = 0; } else { UINode previousNode = ((UINode)viewPane.get((String)((Vector)nodeLevelList.elementAt(level-1)).elementAt(indexOfPreviousNode))); int previousNodeYPosition = previousNode.getNodePosition().getYPos(); // ALLOW FOR FACT VIEW MIGHT BE SCALED int nodeHeight = currentNode.getHeight(); double scale = viewPane.getScale(); Point p = UIUtilities.scalePoint(nodeHeight, nodeHeight, scale); nodeHeight = p.x; // ALLOW FOR FACT VIEW MIGHT BE SCALED int previousNodeHeight = previousNode.getHeight(); Point p2 = UIUtilities.scalePoint(previousNodeHeight, previousNodeHeight, scale); previousNodeHeight = p2.x; if ( (currentNodeYPosition - previousNodeYPosition - VERT_SEP - previousNodeHeight) < compactAmount) { compactAmount = currentNodeYPosition - previousNodeYPosition - VERT_SEP - previousNodeHeight; } } Vector childNodeList = (Vector)htNodesBelow.get(nodeId); if (childNodeList != null) { compactAmount = checkCompact(viewFrame, (String)childNodeList.elementAt(0),compactAmount, compactDoneForNodes); } if (compactAmount < 0) { compactAmount = 0; } return compactAmount; } /** * Helper Method. This method compacts the node with the given id. * @param viewFrame com.compendium.ui.UIViewFrame, the frame of the node to compact. * @param nodeId, the id of the node to compact. * @param compactAmount, the amount to compact by. * @param compactDoneForNodes, nodes already compacted. */ private void compact(UIViewFrame viewFrame, String nodeId, int compactAmount, Hashtable compactDoneForNodes) { UIViewPane viewPane = ((UIMapViewFrame)viewFrame).getViewPane(); Boolean isCompactDone = (Boolean)compactDoneForNodes.get(nodeId); if (isCompactDone != null) { if (isCompactDone.booleanValue() == true) { return; } } UINode node = ((UINode)viewPane.get(nodeId)); Point pos = node.getNodePosition().getPos(); int level = ((Integer)htNodesLevel.get(nodeId)).intValue(); int indexOfCurrentNode = ((Vector)nodeLevelList.elementAt(level-1)).indexOf(nodeId); int indexOfPreviousNode = indexOfCurrentNode - 1; int yPosition = 0; if (indexOfPreviousNode > -1) { UINode previousNode = ((UINode)viewPane.get((String)((Vector)nodeLevelList.elementAt(level-1)).elementAt(indexOfPreviousNode))); int previousNodeYPosition = previousNode.getNodePosition().getYPos(); // ALLOW FOR FACT VIEW MIGHT BE SCALED int previousNodeHeight = previousNode.getHeight(); Point p = UIUtilities.scalePoint(previousNodeHeight, previousNodeHeight, viewPane.getScale()); previousNodeHeight = p.x; if ((pos.y - compactAmount) < (previousNodeYPosition + previousNodeHeight + VERT_SEP)) { yPosition = previousNodeYPosition + previousNodeHeight + VERT_SEP; } else { yPosition = pos.y - compactAmount; } } else { yPosition = pos.y - compactAmount; } if (yPosition < 0) yPosition = 0; node.getNodePosition().setPos(new Point(pos.x, yPosition)); compactDoneForNodes.put(nodeId, new Boolean(true)); Vector nodeList = (Vector)htNodesBelow.get(nodeId); if (nodeList != null) { for (int i = 0; i < nodeList.size(); i++) { if (nodeId.equals((String)htNodesAbove.get(nodeList.elementAt(i)))) { compact(viewFrame, (String) nodeList.elementAt(i), compactAmount, compactDoneForNodes); } } } } //******** UNDO / REDO **************// /** * Undo the last arrange for the given frame * @param viewFrame com.compendium.ui.UIViewFrame, the frame to undo the arrange for. */ public void undoArrange(UIViewFrame viewFrame) { UIViewPane pane = ((UIMapViewFrame)viewFrame).getViewPane(); for(Enumeration e = nodePositionsCloneHashtable.keys(); e.hasMoreElements();) { String nodeID = (String)e.nextElement(); UINode uinode = (UINode)pane.get(nodeID); NodePosition np = (NodePosition)nodePositionsCloneHashtable.get(nodeID); Point loc = UIUtilities.transformPoint(np.getXPos(), np.getYPos(), pane.getScale()); uinode.setBounds(loc.x, loc.y, uinode.getWidth(), uinode.getHeight()); uinode.updateLinks(); try { viewFrame.getView().setNodePosition(nodeID, np.getPos()); } catch(Exception ex) { log.info("Error: (UIArrange.undoArrange) \n\n"+ex.getMessage()); //$NON-NLS-1$ } } pane.repaint(); } /** * Redo the last arrange for the given frame * @param viewFrame com.compendium.ui.UIViewFrame, the frame to undo the arrange for. */ public void redoArrange(UIViewFrame viewFrame) { UIViewPane pane = ((UIMapViewFrame)viewFrame).getViewPane(); for(Enumeration e = nodePositionsCloneHashtableForRedo.keys(); e.hasMoreElements();) { String nodeID = (String)e.nextElement(); UINode uinode = (UINode)pane.get(nodeID); NodePosition np = (NodePosition)nodePositionsCloneHashtableForRedo.get(nodeID); Point loc = UIUtilities.transformPoint(np.getXPos(), np.getYPos(), pane.getScale()); uinode.setBounds(loc.x, loc.y, uinode.getWidth(), uinode.getHeight()); uinode.updateLinks(); try { viewFrame.getView().setNodePosition(nodeID, np.getPos()); } catch(Exception ex) { log.info("Error: (UIArrange.redoArrange) \n\n"+ex.getMessage()); //$NON-NLS-1$ } } pane.repaint(); } /** * Return the list of node levels. * @return Vector, the node level list. */ public Vector getNodeLevelList() { return nodeLevelList; } /** * Return the list of nodes below. * @return Hashtable, the nodes below list. */ public Hashtable getNodesBelow() { return htNodesBelow; } /** * Return the list of nodes. * @return Hashtable, the nodes. */ public Hashtable getNodes() { return htNodes; } /** * Return the list of nodes mapped to levels. * @return Hashtable, the list of nodes mapped to levels. */ public Hashtable getNodesLevel() { return htNodesLevel; } }