/* * #! * Ontopia Vizigator * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * 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. * !# */ package net.ontopia.topicmaps.viz; import java.awt.Color; import java.awt.EventQueue; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.Vector; import javax.swing.Icon; import javax.swing.JDialog; import javax.swing.JOptionPane; import javax.swing.JProgressBar; import net.ontopia.topicmaps.core.AssociationIF; import net.ontopia.topicmaps.core.AssociationRoleIF; import net.ontopia.topicmaps.core.TopicNameIF; import net.ontopia.topicmaps.core.TMObjectIF; import net.ontopia.topicmaps.core.TopicIF; import net.ontopia.topicmaps.core.TopicMapIF; import net.ontopia.topicmaps.core.VariantNameIF; import net.ontopia.topicmaps.core.index.ClassInstanceIndexIF; import net.ontopia.topicmaps.impl.remote.RemoteTopicMapStore; import net.ontopia.topicmaps.utils.tmrap.TopicIndexIF; import net.ontopia.topicmaps.viz.TMClassInstanceAssociation.Key; import net.ontopia.topicmaps.viz.VizController.VizHoverHelpManager; import net.ontopia.utils.CollectionUtils; import net.ontopia.utils.OntopiaRuntimeException; import com.touchgraph.graphlayout.Edge; import com.touchgraph.graphlayout.LocalityUtils; import com.touchgraph.graphlayout.Node; import com.touchgraph.graphlayout.TGException; import com.touchgraph.graphlayout.TGPaintListener; import com.touchgraph.graphlayout.TGPanel; import com.touchgraph.graphlayout.graphelements.GESUtils; import com.touchgraph.graphlayout.graphelements.GraphEltSet; import com.touchgraph.graphlayout.graphelements.Locality; /** * INTERNAL: Maintains the TouchGraph view of the topic map. */ public class TopicMapView { private TopicMapIF topicmap; private TGPanel tgPanel; protected VizTopicMapConfigurationManager configman; protected VizController controller; private ClassInstanceIndexIF typeix; private int locality; private ArrayList nodesByType; private ArrayList nodeTypeIndex; protected ArrayList newNodes = new ArrayList(); private TMAbstractNode ghost = null; private int associationScopeFilterStrictness = VizTopicMapConfigurationManager.SHOW_ALL_ASSOCIATION_SCOPES; // Due to object redirection as a result of TopicMap merging in the "remote" // implementation, we cannot use Topics or Assocaitions as keys in HashMaps. // Therefore we have a list of types, the index of which gives the index of // the corresponding type collection in @objectsByType list. protected ArrayList objectTypeIndex; protected ArrayList objectsByType; protected TopicIF currentScopingTopic; protected Debug debug; Collection nodesUpdateCount; public PerformanceStat stat; public PerformanceStat stat1; MotionKiller motionKiller; VizigatorUser vizigatorUser; /** * Creates the view and updates the TGPanel to show the new view. The TGPanel * may be showing an old view. */ public TopicMapView(VizController controller, TopicMapIF topicmap, TGPanel tgPanel, VizTopicMapConfigurationManager configman) { this.controller = controller; this.topicmap = topicmap; this.tgPanel = tgPanel; this.configman = configman; debug = new Debug(); nodesUpdateCount = new HashSet(); stat = new PerformanceStat("loadNode"); stat.init(); stat1 = new PerformanceStat("createAssociations"); stat1.init(); motionKiller = new MotionKiller(getTGPanel(), 1000); vizigatorUser = new VizigatorUser(controller, 300); maxTopicNameLength = this.configman.getMaxTopicNameLength(); init(); } private void init() { typeix = (ClassInstanceIndexIF) topicmap.getIndex( "net.ontopia.topicmaps.core.index.ClassInstanceIndexIF"); locality = controller.getDefaultLocality(); objectsByType = new ArrayList(); objectTypeIndex = new ArrayList(); nodesByType = new ArrayList(); nodeTypeIndex = new ArrayList(); setPanelBackgroundColour(configman.getPanelBackgroundColour()); } public void updateDisplay() { if (ghost != null && ghost != getFocusNode()) hideNode(ghost); updateAssociationCountForAllTopics(); resetDamper(); } public void updateDisplayLazily() { if (ghost != null && ghost != getFocusNode()) hideNode(ghost); updateAssociationCountForMarkedTopics(); resetDamper(); } public void updateDisplayNoWork() { nodesUpdateCount.clear(); resetDamper(); } public void resetDamper() { getTGPanel().resetDamper(); motionKiller.waitFor(2000, 3000); } public void retainNodes(Collection nodes) { // For each topic type. Iterator nodesByTypeIt = nodesByType.iterator(); while (nodesByTypeIt.hasNext()) { List currentType = (List)nodesByTypeIt.next(); // Remove from currentType any node that is not in nodes. currentType.retainAll(nodes); } } public void retainObjects(Collection nodes) { // For each object type. Iterator objectsByTypeIt = objectsByType.iterator(); while (objectsByTypeIt.hasNext()) { List currentType = (List)objectsByTypeIt.next(); // Remove from currentType any node that is not in nodes. currentType.retainAll(nodes); } } // --- view modifications --------------------------------------------- /** * Set the locality to a given value and adds/removes nodes where appropriate. * * After a locality increase, all nodes and edges within locality's reach * in addition to all nodes and edges that were there before will be visible. * * After a locality decrease, all nodes and edges that were visible before and * that are still within locality's reach will be visible. * @param newLocality The new locality. */ public void setLocality(int newLocality) { int oldLocality = locality; locality = newLocality; if (getFocusNode() != null) { // Ensure all nodes within the new locality are loaded. // Get edges beyond locality's reach. // Only create new edges and nodes if locality has increased. loadNodesInLocality(getFocusNode(), oldLocality < newLocality, newLocality < oldLocality); } } /** * If create is true, adds all nodes within locality's reach that were not * already visible. * Then (independent of 'create') collects all edges just beyond locality's * reach and returns them. * An edge with both end-points within locality's reach is counted as within * locality's reach.. * * ALTERNATIVE EDGE ALGORITHM: * An edge is within locality's reach if one end-point is attached to a node * at least one step closer than locality's reach. * * @param startNode The node to start from. * @param create Whether to create new nodes and edges. * @return edges beyond locality's reach. */ public Collection loadNodesInLocality(TMAbstractNode startNode, boolean create, boolean delete) { // Don't do anything if in map view. if (startNode == null) return Collections.EMPTY_SET; // Visit all the nodes no more than 'locality' edges away from focus node. // Do this using a BREADTH-FIRST traversal, to avoid early cut-offs in // cycles in the graph. HashSet visited = new HashSet(getNodeCount()); Collection currentLevel = Collections.singleton(startNode); // Perform a breadth first traversal. Each iteration of this loop is // another level of depth in the search. for (int distance = 0; distance < locality && !currentLevel.isEmpty(); distance++) { Collection nextLevel = new HashSet(getNodeCount()); // Iterate over all nodes at the current level of depth. Iterator currentLevelIt = currentLevel.iterator(); while (currentLevelIt.hasNext()) { TMAbstractNode currentNode = (TMAbstractNode)currentLevelIt.next(); // Load node, adding all neighbour nodes to the view (if create). if (create) createAssociations(currentNode, true, false); // Add edges to the next level. Iterator edgesIt = currentNode.getEdges(); while (edgesIt.hasNext()) nextLevel.addAll(((TMAbstractEdge)edgesIt.next()) .getTargetsFrom(currentNode)); visited.add(currentNode); } currentLevel = nextLevel; } if (useNodeLocality()) { Iterator currentLevelIt = currentLevel.iterator(); // Mark all nodes of the next level as visited (since they're displayed). while (currentLevelIt.hasNext()) { TMAbstractNode currentNode = (TMAbstractNode)currentLevelIt.next(); // TODO: Check whether moving the following line up has had an impact on // the edte/node -oriented algorithms. // Should it only get executed with node orientation? visited.add(currentNode); } } // Collect edges that connect nodes in the last level of depth with nodes // that have not yet been visited (and consequently are not displayed). Collection farEdges = new ArrayList(); Iterator currentLevelIt = currentLevel.iterator(); while (currentLevelIt.hasNext()) { TMAbstractNode currentNode = (TMAbstractNode)currentLevelIt.next(); Iterator edgesIt = currentNode.getEdges(); while (edgesIt.hasNext()) { TMAbstractEdge currentEdge = (TMAbstractEdge)edgesIt.next(); if (!(visited.contains(currentEdge.getOtherEndpt(currentNode)))) farEdges.add(currentEdge); } } // Only delete edges beyond locality's reach if locality has decreased. if (delete) { deleteEdges(farEdges); removeDisconnectedNodes(); } currentLevelIt = currentLevel.iterator(); while (currentLevelIt.hasNext()) { TMAbstractNode currentNode = (TMAbstractNode)currentLevelIt.next(); if (currentNode instanceof TMAssociationNode) { createAllRoles((TMAssociationNode)currentNode, false); } if (useNodeLocality()) { // Load node, adding all neighbour nodes to the view (if create). if (create) createAssociations(currentNode, false, false); } } return farEdges; } public void setTypeFont(TopicIF type, Font font) { // Set the font in both Associations and Topic objects // Associations Iterator it = getObjectsFor(type).iterator(); while (it.hasNext()) { VizTMObjectIF association = (VizTMObjectIF) it.next(); association.setFont(font); } // Topics it = getTopicNodesFor(type).iterator(); while (it.hasNext()) { TMAbstractNode node = (TMAbstractNode) it.next(); if (isShowType(node, type)) node.setFont(font); } } public void setTopicTypeShape(TopicIF type, int shape) { Iterator it = getTopicNodesFor(type).iterator(); while (it.hasNext()) { TMAbstractNode node = (TMAbstractNode) it.next(); if (isShowType(node, type)) node.setType(shape); } } public void setTypeColor(TopicIF type, Color c) { // Change the colour of the association objects AND topic objects // Association objects: Iterator it = getObjectsFor(type).iterator(); while (it.hasNext()) { VizTMObjectIF association = (VizTMObjectIF) it.next(); association.setColor(c); } // Topic objects it = getTopicNodesFor(type).iterator(); while (it.hasNext()) { TMAbstractNode node = (TMAbstractNode) it.next(); if (isShowType(node, type)) node.setBackColor(c); } } public void setTopicTypeShapePadding(TopicIF type, int value) { Iterator it = getTopicNodesFor(type).iterator(); while (it.hasNext()) { TMTopicNode node = (TMTopicNode) it.next(); if (isShowType(node, type)) node.setShapePadding(value); } } public void setTypeIcon(TopicIF type, Icon icon) { // Set the font in both Associations and Topic objects // Associations Iterator it = getObjectsFor(type).iterator(); while (it.hasNext()) { VizTMObjectIF association = (VizTMObjectIF) it.next(); association.setIcon(icon); } // Toipics it = getTopicNodesFor(type).iterator(); while (it.hasNext()) { TMTopicNode node = (TMTopicNode) it.next(); if (isShowType(node, type)) node.setIcon(icon); } } /** * Returns true iff 'type' is the appropriate config type for 'node'. * @param node The node to get configuration for. * @param type The type to check for appropriateness. * @return True iff 'type is the appropriate config type for 'node'. */ private boolean isShowType(TMAbstractNode node, TopicIF type) { if (!(node instanceof TMTopicNode)) return true; TopicIF primaryType = getPrimaryTypeFor((TMTopicNode)node); if (primaryType == null) return type == null; return primaryType.equals(type); } public void updateType(TopicIF type) { setTypeFont(type, controller.getTypeFont(type)); setTopicTypeShape(type, controller.getTopicTypeShape(type)); setTypeColor(type, controller.getTopicTypeColor(type)); setTopicTypeShapePadding(type, controller.getTopicTypeShapePadding(type)); setTypeIcon(type, controller.getTypeIcon(type)); tgPanel.repaint(); } private List getObjectsFor(TopicIF type) { int index = objectTypeIndex.indexOf(type); if (index == -1) return Collections.EMPTY_LIST; return (List) objectsByType.get(index); } protected List getTopicNodesFor(TopicIF type) { int index = nodeTypeIndex.indexOf(type); if (index == -1) return Collections.EMPTY_LIST; return (List) nodesByType.get(index); } public void setAssociationTypeShape(TopicIF type, int shape) { Iterator it = getObjectsFor(type).iterator(); while (it.hasNext()) { VizTMObjectIF association = (VizTMObjectIF) it.next(); association.setShape(shape); } } public void setAssociationTypeLineWeight(TopicIF type, int lineWeight) { Iterator it = getObjectsFor(type).iterator(); while (it.hasNext()) { VizTMObjectIF association = (VizTMObjectIF) it.next(); association.setLineWeight(lineWeight); } } protected void focusNode(TMAbstractNode node) { tgPanel.setSelect(node); // Add nodes and edges within locality. loadNodesInLocality(node, true, true); } public void clearFocusNode() { // Ensure all nodes are loaded buildAll(); try { tgPanel.setLocale(getFocusNode(), LocalityUtils.INFINITE_LOCALITY_RADIUS); tgPanel.clearSelect(); } catch (TGException e) { throw new OntopiaRuntimeException(e); } } /** * Called when a new configuration has been loaded. Switches the view over to * the new configuration. */ public void setConfigManager(VizTopicMapConfigurationManager configman) { this.configman = configman; locality = 1; nodesByType = new ArrayList(); nodeTypeIndex = new ArrayList(); objectTypeIndex = new ArrayList(); objectsByType = new ArrayList(); build(); } /** * Delete a node, all incident edges and all nodes and edges that no longer * have a path to the focus node as a consequence of this. * @param node The base node to delete. */ public void hideNode(TMAbstractNode node) { // NOTE: THIS METHOD IS USED A LOT BY OTHER METHODS IN TopicMapView and // VisController. THEREFORE CHANGES TO THIS CLASS SHOULD BE MADE WITH // GREAT CARE. // Specifically VizController.hideNode(), VizController.collapseNode() // and TopicMapView.setTopicTypeVisible() rely on hideNode functioning // WITH THE CURRENT BEHAVIOUR! // Workaround that avoids mouseover icon hanging around after hiding node. if (node instanceof TMAssociationNode) ((TMAssociationNode)node).removeMouseoverIcon(); Collection incidentEdges = new ArrayList(); Iterator edgesIt = node.getEdges(); while (edgesIt.hasNext()) { TMAbstractEdge currentEdge = (TMAbstractEdge)edgesIt.next(); incidentEdges.add(currentEdge); // These are the only nodes that need updating both in map/topic view. // The nodes removed in removeDisconnectedNodes are of no importance, // since they are not adjacent to any nodes in the display. nodesUpdateCount.add(currentEdge.getOtherEndpt(node)); } deleteEdges(incidentEdges); if (getFocusNode() == null) { if (node instanceof TMAssociationNode) { ((Locality) tgPanel.getGES()).deleteNode(node); deleteNode((TMAssociationNode)node); } else if (node instanceof TMTopicNode) { ((Locality) tgPanel.getGES()).deleteNode(node); deleteNode((TMTopicNode)node); } } else removeDisconnectedNodes(); } public static String fullName(Node node) { if (node == null) return null; String name = node.getLabel(); if (node instanceof TMTopicNode) name = ((TMTopicNode)node).getTopicName(); return name; } public void headedDebug(String header, Object object) { if (!VizDebugUtils.isDebugEnabled()) return; VizDebugUtils.debug("=====================" + header + "====================="); VizDebugUtils.debug("---- (focusNode: " + fullName(getFocusNode()) + " ---)"); if (object != null) { VizDebugUtils.debug("---- (Object class: " + object.getClass().getName() + ")"); if (object instanceof Node) VizDebugUtils.debug("---- (node fulltext: " + fullName((Node)object) + " ---)"); else if (object instanceof Edge) VizDebugUtils.debug("---- (edge id: " + ((Edge)object).getID() + " ---)"); else VizDebugUtils.debug(" ---- (Object stringified: " + controller.getStringifier() .toString(object) + " ---)"); } outputDebugInfo("count"); } /** * NOTE! The calling mehtod is itself responsible for calling the * method: updateAssociationCountForAllTopics(). */ public void setTopicTypeVisible(TopicIF type, boolean visible) { if (visible) { if (getFocusNode() == null) { // For each topic of the given type. Iterator topicsIt = typeix.getTopics(type).iterator(); while (topicsIt.hasNext()) { TopicIF currentTopic = (TopicIF)topicsIt.next(); TMTopicNode newNode = assertNode(currentTopic, true); controller.loadNode(newNode); } } // Do nothing in graph view. The graph is being recomputed. No more nodes // or edges need adding. } else { int index = nodeTypeIndex.indexOf(type); if (index != -1) { List objects = new ArrayList(getTopicNodesFor(type)); ArrayList nodesOfThisType = new ArrayList(); nodesByType.set(index, nodesOfThisType); if (objects.contains(getFocusNode())) { nodesOfThisType.add(getFocusNode()); ghost = getFocusNode(); } Iterator it = objects.iterator(); while (it.hasNext()) { TMTopicNode node = (TMTopicNode) it.next(); nodesUpdateCount.add(node); // delete the incident edges Collection edgesToDelete = new ArrayList(); Iterator edgesIt = node.getEdges(); while (edgesIt.hasNext()) { Edge edge = (Edge)edgesIt.next(); Node farNode = edge.getOtherEndpt(node); nodesUpdateCount.add(farNode); edgesToDelete.add(edge); } deleteEdges(edgesToDelete); if (node != getFocusNode()) { // The current functionality is that if a type is made // invisible and a topic of that type has multiple types, then it // should still be invisible. // Therefore we need to remove the topic from the other // type indexes it is included in. for (Iterator iter = getValidTypesFor(node.getTopic()) .iterator(); iter.hasNext();) { TopicIF aType = (TopicIF) iter.next(); ((List) nodesByType.get(nodeTypeIndex.indexOf(aType))) .remove(node); } tgPanel.deleteNode(node); // this also removes the edges } } if (getFocusNode() != null) removeDisconnectedNodes(); } } } /** * NOTE: The calling method is responsible for calling the * method: updateAssociationCountForAllTopics(); * @param type The type to set (in)visible. * @param visible true if 'type' should be set visible. Otherwise false. */ public void setAssociationTypeVisible(TopicIF type, boolean visible) { if (visible) { if (getFocusNode() == null) { // create edges for the associations if (type != configman.getTypeInstanceType()) { Iterator it = typeix.getAssociations(type).iterator(); while (it.hasNext()) { AssociationIF assoc = (AssociationIF) it.next(); if (configman.isVisible(assoc) && findObject(assoc, assoc.getType()) == null) { // FIXME: With 2nd param false filter default association type out // followed by filter default assocication type in does not // recreate turnary associations. makeAssociation(assoc, true); } } } else { Iterator it = typeix.getTopicTypes().iterator(); while (it.hasNext()) { TopicIF ttype = (TopicIF) it.next(); if (!configman.isVisible(ttype) || !configman.isTopicTypeVisible(ttype)) continue; TMTopicNode tnode = getNode(ttype); if (tnode != null) { Iterator it2 = typeix.getTopics(ttype).iterator(); while (it2.hasNext()) { TopicIF instance = (TopicIF) it2.next(); if (configman.isVisible(instance)) { TMTopicNode node = getNode(instance); if (node != null) makeTypeInstanceEdge(node, tnode); } } } for (Iterator nodesIt = getTopicNodesFor(ttype).iterator(); nodesIt .hasNext();) { setValidAssociationCountFor((TMTopicNode) nodesIt.next()); } } } } } else { // delete the edges for the associations Collection edges = getObjectsFor(type); Iterator edgesIt = new ArrayList(edges).iterator(); while (edgesIt.hasNext()) { VizTMObjectIF edge = (VizTMObjectIF)edgesIt.next(); if (edge == getFocusNode() && edge instanceof TMAssociationNode) ghost = (TMAssociationNode)edge; else { deleteEdgeUndoable(edge); } } } // Make sure there are no "islands" not (in)directly connected to focus node removeDisconnectedNodes(); } private void removeInvisibleEdges() { // For each edge: // If the association of the edge is not visible // Delete the edge. Collection filter = configman.getAssociationScopeFilter(); if (!(filter.size() == 0)) { Iterator associationTypesIt = getAssociationTypes().iterator(); // For each association type while (associationTypesIt.hasNext()) { TopicIF associationType = (TopicIF)associationTypesIt.next(); Collection associationObjects = new ArrayList(getObjectsFor( associationType)); Iterator associationObjectsIt = associationObjects.iterator(); // For each association object (edge) of the current association type while (associationObjectsIt.hasNext()) { VizTMObjectIF associationObject = (VizTMObjectIF)associationObjectsIt .next(); AssociationIF association = null; if (associationObject instanceof TMAssociationNode) association = ((TMAssociationNode)associationObject) .getAssociation(); else if (associationObject instanceof TMAssociationEdge) association = ((TMAssociationEdge)associationObject) .getAssociation(); // Note: No need to handle class-instance associations since they // they have no scope. if (association != null) { Collection associationScope = association.getScope(); // If this is an edge (association) not matching the filter if (!configman.matchesFilter(associationScope, filter)) { // Delete the edge. deleteEdge(associationObject); associationObject.deleteFrom(tgPanel); } } } } removeDisconnectedNodes(); } } private void createVisibleEdges() { // For each association Iterator associationsIt = topicmap.getAssociations().iterator(); while (associationsIt.hasNext()) { AssociationIF association = (AssociationIF)associationsIt.next(); if (association.getType() != configman.getTypeInstanceType()) { // If the edge does not exist, but the association of the edge is visible if (configman.isVisible(association) && findObject(association, association.getType()) == null) { // Get invisible players *before* making the edge. Collection invisiblePlayers = getInvisiblePlayers(association); // Create an edge for the associations makeAssociation(association, false); Iterator invisiblePlayersIt = invisiblePlayers.iterator(); while (invisiblePlayersIt.hasNext()) { TopicIF player = (TopicIF)invisiblePlayersIt.next(); TMTopicNode node = getNode(player); ((Locality) tgPanel.getGES()).removeNode(node); } } } } loadNodesInLocality(getFocusNode(), true, false); } private Set getInvisiblePlayers(AssociationIF association) { Set invisiblePlayers = new HashSet(); Collection roles = association.getRoles(); Iterator rolesIt = roles.iterator(); while (rolesIt.hasNext()) { AssociationRoleIF role = (AssociationRoleIF)rolesIt.next(); TopicIF player = role.getPlayer(); TMTopicNode node = getNode(player); if (node == null || !((Locality)tgPanel.getGES()).contains(node)) invisiblePlayers.add(player); } return invisiblePlayers; } /** * Set the current level of strictness of the association scope filter * and remove/add edges where needed. * @param strictness The new level of strictness */ public void setAssociationScopeFilterStrictness(int strictness) { int SHOW_ALL = VizTopicMapConfigurationManager.SHOW_ALL_ASSOCIATION_SCOPES; int LOOSE = VizTopicMapConfigurationManager.LOOSE_ASSOCIATION_SCOPES; int STRICT = VizTopicMapConfigurationManager.STRICT_ASSOCIATION_SCOPES; if (associationScopeFilterStrictness == SHOW_ALL) { if ((strictness == LOOSE || strictness == STRICT) && configman.getAssociationScopeFilter().size() != 0) removeInvisibleEdges(); } else if (associationScopeFilterStrictness == LOOSE) { if (strictness == SHOW_ALL && configman.getAssociationScopeFilter().size() != 0) createVisibleEdges(); else if (strictness == STRICT && configman.getAssociationScopeFilter().size() != 0) removeInvisibleEdges(); } else if (associationScopeFilterStrictness == STRICT) { if ((strictness == SHOW_ALL || strictness == LOOSE) && configman.getAssociationScopeFilter().size() != 0) createVisibleEdges(); } associationScopeFilterStrictness = strictness; } public void removeAssociationScopeFilterTopic(TopicIF scopingTopic) { int SHOW_ALL = VizTopicMapConfigurationManager.SHOW_ALL_ASSOCIATION_SCOPES; int LOOSE = VizTopicMapConfigurationManager.LOOSE_ASSOCIATION_SCOPES; if (associationScopeFilterStrictness == SHOW_ALL) return; // If this is the last topic in the filter. if (configman.getAssociationScopeFilter().size() == 0) { createVisibleEdges(); updateAssociationCountForAllTopics(); return; } if (associationScopeFilterStrictness == LOOSE) { removeInvisibleEdges(); removeDisconnectedNodes(); } else { // implied: associationScopeFilterStrictness == STRICT createVisibleEdges(); } updateAssociationCountForAllTopics(); } public void addAssociationScopeFilterTopic(TopicIF scopingTopic) { int SHOW_ALL = VizTopicMapConfigurationManager.SHOW_ALL_ASSOCIATION_SCOPES; int LOOSE = VizTopicMapConfigurationManager.LOOSE_ASSOCIATION_SCOPES; if (associationScopeFilterStrictness == SHOW_ALL) return; // If this is the first topic in the filter. if (configman.getAssociationScopeFilter().size() == 1) removeInvisibleEdges(); else if (associationScopeFilterStrictness == LOOSE) createVisibleEdges(); else // implied: associationScopeFilterStrictness == STRICT removeInvisibleEdges(); // Possible optimisation: Instead remove edges that don't have this topic // in their scope. removeDisconnectedNodes(); } public void updateAssociationCountForAllTopics() { for (Iterator iter = (new ArrayList(nodesByType)).iterator(); iter.hasNext();) { ArrayList nodes = new ArrayList((ArrayList)iter.next()); for (Iterator iterator = nodes.iterator(); iterator.hasNext();) { TMTopicNode node = (TMTopicNode) iterator.next(); setValidAssociationCountFor(node); } } for (Iterator iter = (new ArrayList(objectsByType)).iterator(); iter.hasNext();) { ArrayList objects = (ArrayList) iter.next(); for (Iterator iterator = objects.iterator(); iterator.hasNext();) { VizTMObjectIF currentObject = (VizTMObjectIF) iterator.next(); if (currentObject instanceof TMAssociationNode) setValidRoleCountFor((TMAssociationNode)currentObject); } } // No need to subsequently update count for marked nodes, since all nodes // have now been updated. nodesUpdateCount.clear(); } public void updateAssociationCountForMarkedTopics() { Iterator markedTopicNodesIt = nodesUpdateCount.iterator(); while (markedTopicNodesIt.hasNext()) { TMAbstractNode currentNode = (TMAbstractNode)markedTopicNodesIt.next(); if (currentNode instanceof TMTopicNode) setValidAssociationCountFor((TMTopicNode)currentNode); else // currentNode instanceof TMAssociationNode setValidRoleCountFor((TMAssociationNode)currentNode); } nodesUpdateCount.clear(); } public TopicMapIF getTopicMap() { return topicmap; } public Collection getPagesFor(TopicIF topic) { TopicIndexIF ix = ((RemoteTopicMapStore) topicmap.getStore()) .getTopicIndex(); return ix.getTopicPages(topic.getSubjectIdentifiers(), topic.getItemIdentifiers(), topic.getSubjectLocators()); } /** * Returns a collection of all association types in the topic map. Does not * work with remote topic maps. */ public Collection getAssociationTypes() { List types = new ArrayList(typeix.getAssociationTypes()); types.add(configman.getTypeInstanceType()); types.add(configman.defaultAssociationType); return types; } /** * Returns a collection of all topic types in the topic map including null * (untyped). Does not work with remote topic maps. */ public Collection getAllTopicTypesWithNull() { Collection types = getAllTopicTypes(); types.add(null); types.add(configman.defaultType); return types; } /** * Returns a collection of all topic types in the topic map. Does not work * with remote topic maps. */ public Collection getAllTopicTypes() { return new ArrayList(typeix.getTopicTypes()); } // --- internal ------------------------------------------------------- /** * Clears the panel and builds a new view. Assumes there is no existing graph. */ protected void build() { clearModel(); TopicIF startTopic = controller.getStartTopic(topicmap); if (startTopic == null) { Frame parentFrame = JOptionPane.getFrameForComponent(tgPanel); TopicSelectionPrompter prompter = new TopicSelectionPrompter(parentFrame, getAllVisibleTopics(), VizUtils .stringifierFor(currentScopingTopic)); TopicIF selection = prompter.getSelection(); if (selection != null) controller.focusNodeInternal(buildTopic(selection)); else { int result = JOptionPane .showConfirmDialog( parentFrame, Messages.getString("Viz.NoInitialTopic"), Messages.getString("Viz.TopicSelection"), JOptionPane.YES_NO_CANCEL_OPTION); switch (result) { case JOptionPane.CANCEL_OPTION: break; case JOptionPane.YES_OPTION: buildAll(); break; case JOptionPane.NO_OPTION: build(); break; } } updateDisplay(); } else controller.focusNodeInternal(buildTopic(startTopic)); controller.undoManager.reset(); } private Collection getAllVisibleTopics() { Collection topics = topicmap.getTopics(); ArrayList result = new ArrayList(topics.size()); for (Iterator topicsIt = topics.iterator(); topicsIt.hasNext();) { TopicIF topic = (TopicIF) topicsIt.next(); if (configman.isVisible(topic)) result.add(topic); } return result; } protected TMTopicNode buildTopic(TopicIF topic) { TMTopicNode node = assertNode(topic, true); return node; } /** * EXPERIMENTAL: method to redraw the map, without starting a new * thread or displaying the progress bar */ public void buildAllSilent() { final Collection topics = getTopicMap().getTopics(); Iterator it = topics.iterator(); while (it.hasNext()) { TopicIF topic = (TopicIF) it.next(); TMTopicNode node = assertNode(topic, true); if (node != null) createAssociations(node); } } protected void buildAll() { final Collection topics = topicmap.getTopics(); final boolean[] running = new boolean[] { true }; final String[] cancelOptions = new String[] { Messages .getString("Viz.Cancel") }; final JProgressBar progressBar = new JProgressBar(); progressBar.setMinimum(0); progressBar.setMaximum(topics.size()); progressBar.setValue(0); final JOptionPane pane = new JOptionPane(new Object[] { Messages.getString("Viz.BuildingModel"), progressBar }, JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION, null, cancelOptions, null); Frame frame = JOptionPane.getFrameForComponent(tgPanel); final JDialog dialog = new JDialog(frame, Messages .getString("Viz.Information"), true); dialog.setContentPane(pane); dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); dialog.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent we) { running[0] = (JOptionPane .showConfirmDialog( pane, Messages.getString("Viz.CancelBuilding"), Messages.getString("Viz.Question"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.NO_OPTION); } }); pane.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { if (pane.getValue() == cancelOptions[0]) { running[0] = (JOptionPane .showConfirmDialog( pane, Messages.getString("Viz.CancelBuilding"), Messages.getString("Viz.Question"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.NO_OPTION); // We need to reset the panel's value so that the changeListener // will get called again if the user presses the cancel button again. pane.setValue(null); } } }); dialog.pack(); dialog.setLocationRelativeTo(frame); final SwingWorker worker = new SwingWorker() { public Object construct() { Iterator it = topics.iterator(); int max = topics.size(); int counter = 0; int increment = Math.max(1, (topics.size() / 20)); int nextProgress = increment; final int[] progress = new int[] { 0 }; while (it.hasNext() && running[0]) { TopicIF topic = (TopicIF) it.next(); TMTopicNode node = assertNode(topic, true); if (node != null) { createAssociations(node); } counter++; if (counter == nextProgress) { progress[0] = counter; nextProgress = Math.min(nextProgress + increment, max); try { // Updating of the progress bar should allways occure // in the UI thread. // Using invokeLater() here makes the build a tad // faster, but the progressMonitor can become a little // out of sync. Using invokeAndWait() slows down the // load a tad, but means that the progress bar is allways // in sync. EventQueue.invokeAndWait(new Runnable() { public void run() { progressBar.setValue(progress[0]); } }); } catch (Exception e) { throw new OntopiaRuntimeException(e); } } } return null; } public void finished() { dialog.hide(); } }; worker.start(); dialog.show(); } protected TMTopicNode assertNode(TopicIF topic, boolean create) { TMTopicNode node = getNode(topic); if (node == null && create) { controller.loadTopic(topic); if (configman.isVisible(topic)) { node = makeNode(topic); newNodes.add(node); nodesUpdateCount.add(node); } } return node; } private TMTopicNode makeNode(TopicIF topic) { TMTopicNode node = new TMTopicNode(topic, currentScopingTopic, this); // Index by type Iterator it = getValidTypesFor(topic).iterator(); if (!it.hasNext()) // if no type indexNode(node, null); while (it.hasNext()) { TopicIF type = (TopicIF) it.next(); indexNode(node, type); } initializeNode(node); controller.undoManager.addRecovery(node.getDesctructor()); return node; } protected List foregroundQueue = new ArrayList(); private int maxTopicNameLength = VizTopicMapConfigurationManager .DEFAULT_MAX_TOPIC_NAME_LENGTH; protected void queueInForeground(TMAbstractNode node) { foregroundQueue.add(node); } protected void queueInForeground(TMAbstractEdge edge) { foregroundQueue.add(edge); } protected void setHighlightNode(TMAbstractNode node, Graphics g) { controller.setHighlightNode(node, g); } protected void processForegroundQueue(Graphics graphics) { Iterator foregroundQueueIt = foregroundQueue.iterator(); while (foregroundQueueIt.hasNext()) { Object o = foregroundQueueIt.next(); if (o instanceof TMTopicNode) { TMTopicNode topicNode = (TMTopicNode)o; topicNode.miniPaint(graphics, tgPanel); } else if (o instanceof TMAssociationNode) { TMAssociationNode associationNode = (TMAssociationNode)o; associationNode.miniPaint(graphics, tgPanel); } else if (o instanceof TMAbstractNode) { TMAbstractNode abstractNode = (TMAbstractNode)o; abstractNode.paint(graphics, tgPanel); } else if (o instanceof TMAbstractEdge) { TMAbstractEdge abstractEdge = (TMAbstractEdge)o; abstractEdge.paint(graphics, tgPanel); } } foregroundQueue = new ArrayList(); } private void setValidAssociationCountFor(TMTopicNode aNode) { int count = 0; TopicIF topic = aNode.getTopic(); if (aNode == getFocusNode() && !(configman.isVisible(topic))) { // The rule when the focus node is made invisible is that everything *but* // the focus node is hidden from the display. // The rationale is that otherwise everything would be hidden, which is // not very useful. // So in this case, the count is necessarily 0. aNode.setAssociationCount(0); return; } // Standard associations for (Iterator rolesIt = topic.getRoles().iterator(); rolesIt.hasNext();) { AssociationIF association = ((AssociationRoleIF) rolesIt.next()) .getAssociation(); if (configman.isVisible(association) && (arePlayersVisible(association, topic) || association.getRoles().size() != 2)) // NOTE: n-ary associations (n > 2) can be shown even if the players // are not visible, as the players of an n-ary associaiton don't need // to be shown. An n-ary is a self contained node, not just an edge. count++; } if (configman.isAssociationTypeVisible(configman.getTypeInstanceType())) { // Instance-Class associations count = count + getValidVisibleTypesFor(topic).size(); // Class-Instance associations if (configman.isTopicTypeVisible(topic)) count = count + typeix.getTopics(topic).size(); } aNode.setAssociationCount(count); } /** * Return the types for the passed in topic that are not "excluded" */ private List getValidVisibleTypesFor(TopicIF aTopic) { Collection types = aTopic.getTypes(); if (types.isEmpty()) return Collections.EMPTY_LIST; ArrayList result = new ArrayList(types.size()); for (Iterator typesIt = types.iterator(); typesIt.hasNext();) { TopicIF type = (TopicIF) typesIt.next(); if (!isExcludedType(type) && configman.isVisible(type)) result.add(type); } return result; } private void setValidRoleCountFor(TMAssociationNode aNode) { int count = 0; AssociationIF association = aNode.getAssociation(); // The valid association count is the number of roles with players that // are visible. Iterator rolesIt = association.getRoles().iterator(); while (rolesIt.hasNext()) { TopicIF currentPlayer = ((AssociationRoleIF)rolesIt.next()).getPlayer(); if (configman.isVisible(currentPlayer)) count++; } aNode.setRoleCount(count); } private boolean arePlayersVisible(AssociationIF association, TopicIF source) { for (Iterator rolesIt = association.getRoles().iterator(); rolesIt.hasNext();) { TopicIF player = ((AssociationRoleIF) rolesIt.next()).getPlayer(); if (player == null) continue; if (!source.equals(player) && configman.isVisible(player)) return true; } return false; } private void initializeNode(TMTopicNode aNode) { initializeNode(aNode, getPrimaryTypeFor(aNode)); } /** * Get the primary type for the given node. That is, the type to be used for * configuration purposes. * (Previously this just returned the first of the types * that are not excluded, or null if all were excluded.) */ private TopicIF getPrimaryTypeFor(TMTopicNode aNode) { List types = getValidTypesFor(aNode.getTopic()); if (types.isEmpty()) return null; return configman.getTTPriorityManager().highestRankedType(types); } /** * Return the types for the passed in topic that are not "excluded" */ private List getValidTypesFor(TopicIF aTopic) { Collection types = aTopic.getTypes(); if (types.isEmpty()) return Collections.EMPTY_LIST; ArrayList result = new ArrayList(types.size()); for (Iterator typesIt = types.iterator(); typesIt.hasNext();) { TopicIF type = (TopicIF) typesIt.next(); if (!isExcludedType(type)) result.add(type); } return result; } private boolean isExcludedType(TopicIF aType) { return configman.isTypeExcluded(aType); } /** * Initialize a node with the values associatied with the passed in type * * @param node * @param type */ private void initializeNode(TMTopicNode node, TopicIF type) { node.setBackColor(configman.getTopicTypeColor(type)); node.setType(configman.getTopicTypeShape(type)); node.setFont(configman.getTypeFont(type)); node.setIcon(configman.getTypeIcon(type)); node.setShapePadding(configman.getTopicTypeShapePadding(type)); } private void indexNode(TMTopicNode node, TopicIF type) { ArrayList target; int index = nodeTypeIndex.indexOf(type); if (index == -1) { nodeTypeIndex.add(type); target = new ArrayList(); nodesByType.add(target); } else target = (ArrayList) nodesByType.get(index); target.add(node); } protected VizTMObjectIF findObject(Object object, TopicIF type) { Iterator iterator = getObjectsFor(type).iterator(); while (iterator.hasNext()) { VizTMObjectIF target = (VizTMObjectIF) iterator.next(); if (target.represents(object)) return target; } return null; } protected TMClassInstanceAssociation makeTypeInstanceEdge(TMTopicNode instance, TMTopicNode type) { TMClassInstanceAssociation newInstance = new TMClassInstanceAssociation( type, instance, configman.getTypeInstanceType()); initializeObject(newInstance); // Next line not really needed, but keeps things consistent initializeEdge(newInstance); addAssociation(newInstance); controller.undoManager.addRecovery(newInstance.getDesctructor()); return newInstance; } private void makeAssociation(AssociationIF assoc, boolean create) { makeAssociation(assoc, null, create); } /** * Create an associations, or, in the case of an n-ary association, if the * activePlayer is not null, create the associatin node itself and then only * the role that active player is involved in. */ protected VizTMObjectIF makeAssociation(AssociationIF assoc, TMTopicNode activePlayer, boolean create) { VizTMObjectIF retVal = null; Collection roles = assoc.getRoles(); int rolesSize = roles.size(); ArrayList displayableRoles = new ArrayList(rolesSize); Iterator rolesIt = roles.iterator(); while (rolesIt.hasNext()) { AssociationRoleIF role = (AssociationRoleIF) rolesIt.next(); TopicIF player = role.getPlayer(); if (player != null && (rolesSize == 2 || activePlayer == null || player.equals(activePlayer.getTopic()))) { displayableRoles.add(role); } } if (displayableRoles.size() == 0) { return retVal; } if (displayableRoles.size() == 2 && (!((AssociationRoleIF) displayableRoles.get(0)).getPlayer().equals( ((AssociationRoleIF) displayableRoles.get(1)).getPlayer()))) retVal = makeOptimizedAssociation(assoc, displayableRoles, create); else retVal = makeStandardAssociation(assoc, displayableRoles, create); // Add all the role players of the association to the TouchGraph panel. rolesIt = displayableRoles.iterator(); while (rolesIt.hasNext()) { AssociationRoleIF role = (AssociationRoleIF)rolesIt.next(); TopicIF player = role.getPlayer(); TMAbstractNode node = getNode(player); if (node != null) try { tgPanel.addNode(node); } catch (TGException e) { lenientAddNode(node); } } return retVal; } /** * This method was created as a work-around for bug #1898, which happens * when two nodes have the same ID (the second node failing to get added to * tgPanel. * I don't know why two nodes sometimes get the same ID, which is important * information needed to avoid it, hence this work-around. * Adds a systematic suffix and tries to add the node until no exception * occurrs. * @param node The node that should be added to tgPanel. */ protected void lenientAddNode(TMAbstractNode node) { boolean succeeded = false; // Find a potential duplicate Node collidingNode = tgPanel.getGES().findNode(node.getID()); if (collidingNode != null && collidingNode.getClass().getName() == node.getClass().getName()) { // First try to find the collision. If found, remove it and try again if (node instanceof TMTopicNode) { TMTopicNode tNode = (TMTopicNode)node; TMTopicNode tCollNode = (TMTopicNode)collidingNode; if (tNode.getTopic().equals(tCollNode.getTopic())) { return; } } } while (!succeeded) { String id = node.getID(); try { tgPanel.addNode(node); succeeded = true; } catch (TGException e) { // Check the message in case the TGException occurred for another reason String msg = e.getMessage(); if (msg.equals("node ID '" + id + "' already exists.")) VizDebugUtils.debug("********** Caught duplicate node id error."); else throw new OntopiaRuntimeException(e); } // Generate a new, hopefully unique ID. String midFix = "_vizigator_"; int index = id.lastIndexOf(midFix); if (index == -1) { id = id + midFix + 1; } else { int suffix = Integer.parseInt(id.substring(index + midFix.length())); suffix++; id = id.substring(0, index) + midFix + suffix; } node.setID(id); } } /** * Find the node of the given TMObjectIF. If no such node exists, then it * should be created. Then set that node to be the focus node, recomputing * the graph from this new focus node. * Supports TMObjectsIFs instanceof TopicIF or AssociationIF. * @param tmObject from which to create the node. */ protected void setFocusNodeOf(TMObjectIF tmObject) { TMAbstractNode focusNode; if (tmObject instanceof TopicIF) { focusNode = assertNode((TopicIF)tmObject, true); } else if (tmObject instanceof AssociationIF) { AssociationIF assoc = (AssociationIF)tmObject; VizTMObjectIF target = (VizTMObjectIF) findObject(assoc, assoc.getType()); if (target == null) focusNode = makeStandardAssociation(assoc, assoc.getRoles(), true); else { if (!(target instanceof TMAssociationNode)) throw new OntopiaRuntimeException("Internal error! Got an " + "association node from the focus node history, " + "that is not a TMAssociationNode: " + tmObject.getClass().getName()); focusNode = (TMAssociationNode)target; } } else throw new OntopiaRuntimeException("Unsupported TMObjectIF type in " + "setFocusNodeOf operation: " + tmObject.getClass().getName()); controller.focusNodeInternal(focusNode); } private TMAssociationNode makeStandardAssociation(AssociationIF assoc, Collection roles, boolean create) { TMAssociationNode target = null; if (create) { target = new TMAssociationNode(assoc, currentScopingTopic, this); newNodes.add(target); initializeAssociation(target); addAssociation(target); for (Iterator rolesIt = roles.iterator(); rolesIt.hasNext();) { makeRole(target, (AssociationRoleIF) rolesIt.next(), create); } controller.undoManager.addRecovery(target.getDesctructor()); } return target; } protected TMRoleEdge makeRole(TMAssociationNode assoc, AssociationRoleIF role, boolean create) { TMTopicNode target = assertNode(role.getPlayer(), create); if (target != null) { TMRoleEdge instance = new TMRoleEdge(assoc, target, role, currentScopingTopic); initializeObject(instance); initializeEdge(instance); addAssociation(instance); controller.undoManager.addRecovery(instance.getDesctructor()); return instance; } return null; } private VizTMObjectIF makeOptimizedAssociation(AssociationIF assoc, Collection roles, boolean create) { // Optimized, i.e. those with only two players, are represented by Edges Iterator it = roles.iterator(); TMTopicNode start = assertNode(((AssociationRoleIF) it.next()).getPlayer(), create); TMTopicNode end = assertNode(((AssociationRoleIF) it.next()).getPlayer(), create); TMAssociationEdge instance = null; if (start != null && end != null) { instance = new TMAssociationEdge(start, end, assoc, currentScopingTopic); initializeAssociation(instance); controller.undoManager.addRecovery(instance.getDesctructor()); initializeEdge(instance); addAssociation(instance); } return instance; } /** * Initialize those things only associated with Association Objects * @param anInstance */ protected void initializeAssociation(VizTMAssociationIF anInstance) { initializeObject(anInstance); anInstance.setShouldDisplayScopedAssociationNames(configman .shouldDisplayScopedAssociationNames()); } protected void addAssociation(VizTMObjectIF object) { TopicIF type = object.getTopicMapType(); indexObject(object, type); object.addTo(tgPanel); // Register for HoverHelp getHoverHelpManager().addPaintListener((TGPaintListener) object); } protected VizHoverHelpManager getHoverHelpManager() { return controller.getHoverHelpManager(); } /** * Initialize those things only associated with Edge Objects */ private void initializeEdge(TMAbstractEdge edge) { edge.setShouldDisplayRoleHoverHelp(configman.shouldDisplayRoleHoverHelp()); } /** * Initialize thoes things only associated with all Objects * @param anInstance */ private void initializeObject(VizTMObjectIF object) { TopicIF type = object.getTopicMapType(); object.setColor(configman.getAssociationTypeColor(type)); object.setShape(configman.getAssociationTypeShape(type)); object.setLineWeight(configman.getAssociationTypeLineWeight(type)); object.setFont(configman.getAssociationTypeFont(type)); object.setIcon(configman.getTypeIcon(type)); } private void indexObject(VizTMObjectIF tmAssoc, TopicIF type) { ArrayList target; int index = objectTypeIndex.indexOf(type); if (index == -1) { // Add 'type' to 'objectTypeIndex' in the last position and add a new // corresonding ArrayList at the same (last) position in objectsByType. objectTypeIndex.add(type); target = new ArrayList(); objectsByType.add(target); } else // Get the ArrayList for this type. target = (ArrayList) objectsByType.get(index); target.add(tmAssoc); } /** * Creates all edges for this node, including type -> instance, instance -> * type, and ordinary associations. */ public void createAssociations(TMAbstractNode abstractNode) { createAssociations(abstractNode, true); } public void createAssociations(TMAbstractNode abstractNode, boolean create) { createAssociations(abstractNode, create, true); } /** * Creates all edges for this node, including type -> instance, instance -> * type, and ordinary associations. * If create, then newnodes are created. */ public void createAssociations(TMAbstractNode abstractNode, boolean create, boolean createEdgesToExistingNodes) { stat1.startOp(); if (abstractNode instanceof TMTopicNode) { TMTopicNode topicNode = (TMTopicNode)abstractNode; createAllAssociations(topicNode, create); nodesUpdateCount.add(topicNode); } else if (abstractNode instanceof TMAssociationNode) { TMAssociationNode associationNode = (TMAssociationNode)abstractNode; createAllRoles(associationNode, create); nodesUpdateCount.add(associationNode); } stat1.stopOp(); while (newNodes.isEmpty() == false) { TMAbstractNode newNode = (TMAbstractNode) newNodes.remove(0); nodesUpdateCount.add(newNode); try { tgPanel.addNode(newNode); } catch (TGException e) { lenientAddNode(newNode); } if (createEdgesToExistingNodes) { if (newNode instanceof TMTopicNode) { TMTopicNode topicNode = (TMTopicNode)newNode; if (useNodeLocality()) // Create edges to already existing nodes. createAllAssociations(topicNode, false); } else if (newNode instanceof TMAssociationNode) { // Create roles for topics already in the display. TMAssociationNode associationNode = (TMAssociationNode)newNode; // Create edges to already existing nodes. createAllRoles(associationNode, false); } } } } public int getLocalityAlgorithm() { return configman.getGeneralLocalityAlgorithm(); } public boolean useNodeLocality() { return getLocalityAlgorithm() == VizTopicMapConfigurationManager .NODE_ORIENTED; } public static int NODE_LOCALITY = 0; public static int EDGE_LOCALITY = 1; private void createAllRoles(TMAssociationNode node, boolean create) { Collection roles = new ArrayList(node.getAssociation().getRoles()); Iterator edgesIt = node.getEdges(); while (edgesIt.hasNext()) { TMRoleEdge roleEdge = (TMRoleEdge)edgesIt.next(); roles.remove(roleEdge.getRole()); } Iterator rolesIt = roles.iterator(); while (rolesIt.hasNext()) { // TODO : I think this assignment and subsequent tgPanel.addNode() call // is unnecessary. Adding of new nodes should be handles in a collective // loop. Check that this works correctly, and remove the commented lines // TMTopicNode newNode = makeRole(node, (AssociationRoleIF) rolesIt.next(), create); // if (newNode != null) // try { // tgPanel.addNode(newNode); // } catch (TGException e) { // lenientAddNode(newNode); // } } } private void createAllAssociations(TMTopicNode node, boolean create) { createClassInstanceAssociations(node, create); createActualTopicMapAssociations(node, create); } private void createActualTopicMapAssociations(TMTopicNode node, boolean create) { TopicIF topic = node.getTopic(); Iterator it = new ArrayList(topic.getRoles()).iterator(); while (it.hasNext()) { AssociationRoleIF role = (AssociationRoleIF) it.next(); AssociationIF assoc = role.getAssociation(); VizTMObjectIF target = (VizTMObjectIF) findObject(assoc, assoc.getType()); if (configman.isVisible(role) && findObject(role, assoc.getType()) == null && target != null) makeRole((TMAssociationNode) target, role, create); else if (configman.isVisible(assoc) && target == null) { if (assoc.getRoles().size() != 2) makeAssociation(assoc, node, create); else makeAssociation(assoc, create); } } } private void createClassInstanceAssociations(TMTopicNode node, boolean create) { TopicIF topic = node.getTopic(); TopicIF classInstanceType = configman.getTypeInstanceType(); if (configman.isAssociationTypeVisible(classInstanceType)) { // Add Instance-Class associations Iterator it = getValidTypesFor(topic).iterator(); while (it.hasNext()) { TopicIF type = (TopicIF) it.next(); if (configman.isTopicTypeVisible(type)) { TMTopicNode typeNode = assertNode(type, create); if (typeNode != null) { if (findObject(new Key(type, topic), classInstanceType) == null) { makeTypeInstanceEdge(node, typeNode); } } } } // Add Class-Instance associations Collection instances = typeix.getTopics(topic); if (!instances.isEmpty() && !configman.isTypeExcluded(topic)) { it = instances.iterator(); while (it.hasNext()) { TopicIF instance = (TopicIF) it.next(); TMTopicNode instanceNode = assertNode(instance, create); if (instanceNode != null) { if (findObject(new Key(topic, instance), classInstanceType) == null) { makeTypeInstanceEdge(instanceNode, node); } } } } } } public void deleteEdges(Collection edges) { Iterator edgesIt = edges.iterator(); while (edgesIt.hasNext()) { TMAbstractEdge currentEdge = (TMAbstractEdge)edgesIt.next(); deleteEdgeUndoable(currentEdge); } } public void deleteEdgeUndoable(VizTMObjectIF edge) { controller.undoManager.addRecovery(edge.getRecreator()); deleteEdge(edge); } public void deleteEdge(VizTMObjectIF vizTMObject) { getHoverHelpManager().removePaintListener((TGPaintListener)vizTMObject); // unindex by type ((ArrayList) objectsByType.get(objectTypeIndex.indexOf(vizTMObject .getTopicMapType()))).remove(vizTMObject); if (vizTMObject instanceof TMAssociationNode) { // Delete n-ary association (n > 2). TMAssociationNode associationNode = (TMAssociationNode)vizTMObject; deleteNode(associationNode); tgPanel.deleteNode(associationNode); } else if (vizTMObject instanceof TMAbstractEdge) { TMAbstractEdge edge = (TMAbstractEdge)vizTMObject; // Schedule all adjacent nodes for count update. nodesUpdateCount.add(edge.getFrom()); nodesUpdateCount.add(edge.getTo()); // Delete role in n-ary association. tgPanel.deleteEdge(edge); } } public void deleteSingleEdge(VizTMObjectIF edge) { deleteEdgeUndoable(edge); removeDisconnectedNodes(); } public void deleteNode(TMAssociationNode node) { getHoverHelpManager().removePaintListener(node); // Schedule all adjacent nodes for count update. Iterator edgesIt = node.getEdges(); while (edgesIt.hasNext()) { nodesUpdateCount.add(((TMRoleEdge)edgesIt.next()).getOtherEndpt(node)); } // unindex by type ((ArrayList) objectsByType.get(objectTypeIndex.indexOf(node .getTopicMapType()))).remove(node); } public void deleteNode(TMTopicNode node) { // I'm not sure if there's a way to find the correct type of the topic, as // there may be multiple. If there is a way, it would be a great // improvement as the node could just be removed from the appropriate // collection. Iterator nodesByTypeIt = nodesByType.iterator(); while (nodesByTypeIt.hasNext()) { ((Collection)nodesByTypeIt.next()).remove(node); } } private TMTopicNode getNode(TopicIF topic) { Iterator iterator = getValidTypesFor(topic).iterator(); if (iterator.hasNext()) { while (iterator.hasNext()) { TMTopicNode node = getTopicNode(topic, (TopicIF) iterator.next()); if (node != null) return node; } return null; } return getTopicNode(topic, null); } protected TMAssociationEdge getEdge(AssociationIF association) { TopicIF type = association.getType(); TMAssociationEdge edge = null; Iterator iter = getObjectsFor(type).iterator(); while (edge == null && iter.hasNext()) { VizTMObjectIF element = (VizTMObjectIF)iter.next(); // Need to check if element is TMAssociationEdge, as there may well exist // turnary associations of the same type. if (element instanceof TMAssociationEdge && ((TMAssociationEdge)element).getAssociation().equals(association)) edge = (TMAssociationEdge)element; } if (edge != null) return edge; return null; } protected TMRoleEdge getEdge(AssociationRoleIF role) { TopicIF type = role.getAssociation().getType(); TMRoleEdge edge = null; Iterator iter = getObjectsFor(type).iterator(); while (edge == null && iter.hasNext()) { VizTMObjectIF element = (VizTMObjectIF)iter.next(); // Need to check if element is TMAssociationEdge, as there may well exist // turnary associations of the same type. if (element instanceof TMRoleEdge && ((TMRoleEdge)element).getRole().equals(role)) edge = (TMRoleEdge)element; } if (edge != null) return edge; return null; } protected TMClassInstanceAssociation getEdge(TopicIF type, TopicIF instance) { return (TMClassInstanceAssociation)findObject( new Key(type, instance), configman.getTypeInstanceType()); } protected TMAssociationNode getNode(AssociationIF association) { TopicIF type = association.getType(); TMAssociationNode node = getAssociationNode(association, type); if (node != null) return node; return null; } private TMTopicNode getTopicNode(TopicIF topic, TopicIF type) { Iterator iter = getTopicNodesFor(type).iterator(); while (iter.hasNext()) { TMTopicNode element = (TMTopicNode) iter.next(); if (element.getTopic().equals(topic)) return element; } return null; } private TMAssociationNode getAssociationNode(AssociationIF assoc, TopicIF type) { Iterator iter = getObjectsFor(type).iterator(); while (iter.hasNext()) { VizTMObjectIF element = (VizTMObjectIF)iter.next(); // Need to check if element is TMAssociationNode, as there may well exist // binary associations of the same type. if (element instanceof TMAssociationNode && ((TMAssociationNode)element).getAssociation().equals(assoc)) return (TMAssociationNode)element; } return null; } public void shouldDisplayRoleHoverHelp(boolean newValue) { Iterator objectCollections = objectsByType.iterator(); while (objectCollections.hasNext()) { Iterator objects = ((ArrayList) objectCollections.next()).iterator(); while (objects.hasNext()) { VizTMObjectIF object = (VizTMObjectIF) objects.next(); if (object.isEdge()) ((TMAbstractEdge) object).setShouldDisplayRoleHoverHelp(newValue); } } } public void setMotionKillerEnabled(boolean enabled) { motionKiller.setEnabled(enabled); } public void shouldDisplayScopedAssociationNames(boolean newValue) { Iterator objectCollections = objectsByType.iterator(); while (objectCollections.hasNext()) { Iterator objects = ((ArrayList) objectCollections.next()).iterator(); while (objects.hasNext()) { VizTMObjectIF object = (VizTMObjectIF) objects.next(); if (object.isAssociation()) ((VizTMAssociationIF) object) .setShouldDisplayScopedAssociationNames(newValue); } } } public void setPanelBackgroundColour(Color aColor) { tgPanel.setBackColor(aColor); tgPanel.repaint(); } public void removeDisconnectedNodes() { TMAbstractNode target = getFocusNode(); // In map view do nothing. if (target == null) return; // Get the connected graph starting from target. Graph connectedGraph = getConnectedGraph(target); // Get all nodes not part of connectedGraph. Collection connectedNodes = connectedGraph.getNodes(); Vector disconnectedNodes = new Vector(); Iterator viewNodesIt = tgPanel.getAllNodes(); while (viewNodesIt.hasNext()) { TMAbstractNode currentNode = (TMAbstractNode)viewNodesIt.next(); if (!connectedNodes.contains(currentNode)) disconnectedNodes.add(currentNode); } // Get and filter out any non-TMTopicNode Nodes from connectedNodes. Collection connectedObjects = new ArrayList(); Iterator connectedNodesIt = connectedNodes.iterator(); while (connectedNodesIt.hasNext()) { TMAbstractNode currentNode = (TMAbstractNode)connectedNodesIt.next(); if (!(currentNode instanceof TMTopicNode)) { connectedObjects.add(currentNode); connectedNodesIt.remove(); } } // Get all edges not part of connectedGraph. Collection connectedEdges = connectedGraph.getEdges(); Vector disconnectedEdges = new Vector(); Iterator viewEdgesIt = tgPanel.getAllEdges(); if (viewEdgesIt != null) { while (viewEdgesIt.hasNext()) { TMAbstractEdge currentEdge = (TMAbstractEdge)viewEdgesIt.next(); if (!connectedEdges.contains(currentEdge)) disconnectedEdges.add(currentEdge); } } connectedObjects.addAll(connectedEdges); // Remove all nodes not part of connectedGraph deleteNodes(disconnectedNodes); retainNodes(connectedNodes); retainObjects(connectedObjects); // Remove all edges not part of connectedGraph ((Locality) tgPanel.getGES()).removeEdges(disconnectedEdges); deleteEdges(disconnectedEdges); } private void deleteNodes(Vector nodes) { Iterator nodesIt = nodes.iterator(); while (nodesIt.hasNext()) { TMAbstractNode currentNode = (TMAbstractNode)nodesIt.next(); deleteNode(currentNode); } ((Locality) tgPanel.getGES()).deleteNodes(nodes); } private void deleteNode(TMAbstractNode node) { controller.undoManager.addRecovery(node.getRecreator()); } /** * @param target an arbitrary node of the connected graph to be returned. * @return a Set containing all nodes of the connected graph. */ private Graph getConnectedGraph(TMAbstractNode target) { Set graphNodes = new HashSet(); Set graphEdges = new HashSet(); Graph retVal = new Graph(graphNodes, graphEdges); if (target == null) return retVal; graphNodes.add(target); // Process the edges of the graph, adding edges as new nodes are visited. List edgeQueue = target.getVisibleEdgesList(); while (!edgeQueue.isEmpty()) { TMAbstractEdge edge = (TMAbstractEdge)edgeQueue.remove(0); graphEdges.add(edge); TMAbstractNode currentNode = (TMAbstractNode)edge.getFrom(); // At most one of the nodes of edge may not already be in graphNodes. if (graphNodes.contains(currentNode)) currentNode = (TMAbstractNode)edge.getTo(); // If a node of the edge has not yet been visited. if (!graphNodes.contains(currentNode)) { // Visit the node. graphNodes.add(currentNode); Collection additionalEdges = currentNode.getVisibleEdgesList(); // Don't reprocess the edge just visited. additionalEdges.remove(edge); // Schedule for processing: visible edges incident to currentNode. edgeQueue.addAll(additionalEdges); } } return retVal; } public void setTopicTypeExcluded(TopicIF aType, boolean excluded) { if (excluded) { // aType has been excluded. // - Remove nodes of this type from the local index // - Re-initialize the topics by there new primary type // - If there new primary type is null (i.e. untyped) then // index by this type List objects = getTopicNodesFor(aType); int index = nodeTypeIndex.indexOf(aType); nodesByType.set(index, new ArrayList()); Iterator it = objects.iterator(); while (it.hasNext()) { TMTopicNode node = (TMTopicNode) it.next(); TopicIF primaryType = getPrimaryTypeFor(node); initializeNode(node, primaryType); if (primaryType == null) indexNode(node, null); Key key = new Key(aType, node.getTopic()); // We need to take a copy of the edges collection since // it we may be removing edges below without using the // iterator directly. ArrayList edgesCopy = new ArrayList(node.edgeCount()); for (int i = 0; i < node.edgeCount(); i++) { edgesCopy.add(node.edgeAt(i)); } for (Iterator edgesIt = edgesCopy.iterator(); edgesIt.hasNext();) { TMAbstractEdge edge = (TMAbstractEdge) edgesIt.next(); if (edge.represents(key)) { deleteEdgeUndoable(edge); } } } } else { // aType has been included. // - Check all active objects to see if they are of the included type. // if so, re-index by the included type and re-initialize // by there primary type (which may have changed). ArrayList nodesByTypeCopy = new ArrayList(nodesByType); int ignore = nodeTypeIndex.indexOf(aType); for (int i = 0; i < nodesByTypeCopy.size(); i++) { if (i != ignore) { List objects = (List) nodesByType.get(i); for (Iterator objectsIt = objects.iterator(); objectsIt.hasNext();) { TMTopicNode node = (TMTopicNode) objectsIt.next(); if (node.getTopic().getTypes().contains(aType)) { indexNode(node, aType); initializeNode(node); makeTypeInstanceEdge(node, assertNode(aType, true)); } } } } } } public List performSearch(String searchString) { // Do not search nodes that are not in the current locality. ArrayList results = new ArrayList(); String pattern = searchString.toLowerCase(); for (Iterator typesIt = nodesByType.iterator(); typesIt .hasNext();) { ArrayList collection = (ArrayList) typesIt.next(); for (Iterator topicsIt = collection.iterator(); topicsIt .hasNext();) { TMTopicNode node = (TMTopicNode) topicsIt.next(); if (node.isVisible() && searchTopicFor(pattern, node)) results.add(node); } } return results; } private static boolean matchesIgnoreCase(String source, String pattern) { return source.toLowerCase().indexOf(pattern) != -1; } private boolean searchTopicFor(String pattern, TMTopicNode node) { Iterator namesIt = node.getTopic().getTopicNames().iterator(); while (namesIt.hasNext()) { TopicNameIF currentName = (TopicNameIF)namesIt.next(); if (matchesIgnoreCase(currentName.getValue(), pattern)) return true; Iterator variantsIt = currentName.getVariants().iterator(); while (variantsIt.hasNext()) { VariantNameIF currentVariant = (VariantNameIF)variantsIt.next(); if (matchesIgnoreCase(currentVariant.getValue(), pattern)) return true; } } return false; } public int getLocality() { return locality; } public TMTopicNode getStartNode() { TopicIF startTopic = controller.getStartTopic(topicmap); if (startTopic == null) return null; return assertNode(startTopic, true); } public TMAbstractNode getFocusNode() { return (TMAbstractNode) tgPanel.getSelect(); } public TGPanel getTGPanel() { return tgPanel; } public void outputDebugInfo(String operation) { debug.execute(operation); } private int getNodeCount() { // Get the number of nodes currently loaded int count = 0; for (Iterator nodesIt = nodesByType.iterator(); nodesIt.hasNext();) count = count + ((List) nodesIt.next()).size(); return count; } public void setScopingTopic(TopicIF aScope) { currentScopingTopic = aScope; for (Iterator objectsIt = objectsByType.iterator(); objectsIt.hasNext();) { List objects2It = (List) objectsIt.next(); for (Iterator iterator = objects2It.iterator(); iterator.hasNext();) { ((VizTMObjectIF) iterator.next()).setScopingTopic(aScope); } } for (Iterator nodesIt = nodesByType.iterator(); nodesIt.hasNext();) { List objects = (List) nodesIt.next(); for (Iterator objectsIt = objects.iterator(); objectsIt.hasNext();) { ((TMTopicNode) objectsIt.next()).setScopingTopic(aScope); } } tgPanel.repaint(); } public void clearModel() { tgPanel.clearAll(); tgPanel.clearSelect(); tgPanel.setGraphEltSet(new GraphEltSet()); } // -------------- Nested classes and interfaces ------------ private class Graph { private Set nodes; private Set edges; public Graph(Set nodes, Set edges) { this.nodes = nodes; this.edges = edges; } public Set getNodes() { return nodes; } public Set getEdges() { return edges; } } /** * INTERNAL: PRIVATE: Purpose: Output debug information */ protected class Debug extends Object { protected void execute(String operation) { if (operation.equals("count")) counts(); if (operation.equals("gc")) collectGarbage(); if (operation.equals("paint")) paintNodes(true); if (operation.equals("paint-h")) paintNodes(false); if (operation.equals("paint-all")) paintNodes(); if (operation.equals("assoc-v")) outputAssociaitonNames(true); if (operation.equals("h-assoc-h")) outputAssociaitonNames(false); } private void collectGarbage() { Runtime runtime = Runtime.getRuntime(); long total = runtime.totalMemory(); long free = runtime.freeMemory(); System.out.print("Memory - total: "); System.out.print(total); System.out.print(" free: "); System.out.print(free); System.out.print(" used: "); System.out.println(total - free); System.out.println("Performing GC"); runtime.gc(); total = runtime.totalMemory(); free = runtime.freeMemory(); System.out.print("Memory - total: "); System.out.print(total); System.out.print(" free: "); System.out.print(free); System.out.print(" used: "); System.out.println(total - free); } /** * */ private void outputAssociaitonNames(boolean visible) { for (Iterator objectsIt = getObjectsOfType(TMAbstractEdge.class, visible) .iterator(); objectsIt.hasNext();) { VizTMObjectIF object = (VizTMObjectIF) objectsIt.next(); System.out.println(((TMAbstractEdge) object).getMainHoverHelpText()); } for (Iterator objectsIt = getObjectsOfType(TMAssociationNode.class, visible).iterator(); objectsIt.hasNext();) { TMAssociationNode object = (TMAssociationNode) objectsIt.next(); System.out.println(object.getMainText()); } } private void counts() { System.out .println("--------------------------------------" + "--------------------------------------"); System.out.print("Total Node Count = (viz) "); int totalNodeCount = getNodesOfType(Node.class, false).size() + getObjectsOfType(Node.class, false).size(); System.out.print(totalNodeCount); System.out.print(" ("); int totalNodeCount_distinct = getNodesOfType(Node.class, true).size() + getObjectsOfType(Node.class, true).size(); System.out.print(totalNodeCount_distinct); System.out.print(") - (TouchGraph) "); int totalNodeCountTG = getTGNodesOfType(Node.class, false).size(); System.out.print(totalNodeCountTG); System.out.print(" ("); int totalNodeCountTG_distinct = getTGNodesOfType(Node.class, true).size(); System.out.print(totalNodeCountTG_distinct); System.out.println(")"); System.out.print("Visible Node Count (TouchGraph) = "); int visibleNodeCountTG = getTGNodesOfType(Node.class, true, true).size(); System.out.print(visibleNodeCountTG); System.out.print("; Hidden Node Count (TouchGraph) = "); int hiddenNodeCountTG = getTGNodesOfType(Node.class, true, false).size(); System.out.println(hiddenNodeCountTG); System.out.print("Total Edge Count = (viz) "); int totalEdgeCount = getObjectsOfType(Edge.class, false).size(); System.out.print(totalEdgeCount); System.out.print(" ("); int totalEdgeCount_distinct = getObjectsOfType(Edge.class, true).size(); System.out.print(totalEdgeCount_distinct); System.out.print(") - (TouchGraph) "); int totalEdgeCountTG = getTGEdgesOfType(Edge.class, false).size(); System.out.print(totalEdgeCountTG); System.out.print(" ("); int totalEdgeCountTG_distinct = getTGEdgesOfType(Edge.class, true).size(); System.out.print(totalEdgeCountTG_distinct); System.out.println(")"); System.out.print("Visible Edge Count (TouchGraph) = "); int visibleEdgeCount = getTGEdgesOfType(Edge.class, true, true).size(); System.out.print(visibleEdgeCount); System.out.print("; Hidden Edge Count (TouchGraph) = "); int hiddenEdgeCount = getTGEdgesOfType(Edge.class, true, false).size(); System.out.println(hiddenEdgeCount); System.out.print("Total Topic Count = (viz) "); int totalTopicCount = getNodesOfType(TMTopicNode.class, false).size(); System.out.print(totalTopicCount); System.out.print(" ("); int totalTopicCount_distinct = getNodesOfType(TMTopicNode.class, true).size(); System.out.print(totalTopicCount_distinct); System.out.print(") - (TouchGraph) "); int totalTopicCountTG = getTGNodesOfType(TMTopicNode.class, false).size(); System.out.print(totalTopicCountTG); System.out.print(" ("); int totalTopicCountTG_distinct = getTGNodesOfType(TMTopicNode.class, true).size(); System.out.print(totalTopicCountTG_distinct); System.out.println(")"); System.out.print("Total Association Count = (viz) "); int totalAssociationCount = getObjectsOfType(TMAssociationEdge.class, false).size() + getObjectsOfType(TMAssociationNode.class, false).size(); System.out.print(totalAssociationCount); System.out.print(" ("); int totalAssociationCount_distinct = getObjectsOfType(TMAssociationEdge.class, true).size() + getObjectsOfType(TMAssociationNode.class, true).size(); System.out.print(totalAssociationCount_distinct); System.out.print(") - (TouchGraph) "); int totalAssociationCountTG = getTGEdgesOfType(TMAssociationEdge.class, false).size() + getTGNodesOfType(TMAssociationNode.class, false).size(); System.out.print(totalAssociationCountTG); System.out.print(" ("); int totalAssociationCountTG_distinct = getTGEdgesOfType(TMAssociationEdge.class, true).size() + getTGNodesOfType(TMAssociationNode.class, true).size(); System.out.print(totalAssociationCountTG_distinct); System.out.println(")"); System.out.print("Total Role Count = (viz) "); int totalRoleCount = getObjectsOfType(TMRoleEdge.class, false).size(); System.out.print(totalRoleCount); System.out.print(" ("); int totalRoleCount_distinct = getObjectsOfType(TMRoleEdge.class, true).size(); System.out.print(totalRoleCount_distinct); System.out.print(") - (TouchGraph) "); int totalRoleCountTG = getTGEdgesOfType(TMRoleEdge.class, false).size(); System.out.print(totalRoleCountTG); System.out.print(" ("); int totalRoleCountTG_distinct = getTGEdgesOfType(TMRoleEdge.class, true).size(); System.out.print(totalRoleCountTG_distinct); System.out.println(")"); int hiddenNodes = getTGNodesOfType(Node.class, true, false).size(); if (hiddenNodes != 0) System.out.println("WARNING! - There are " + hiddenNodes + " - hidden nodes, but no nodes should ever be hidden!"); int hiddenEdges = getTGEdgesOfType(Edge.class, true, false).size(); if (hiddenEdges != 0) System.out.println("WARNING! - There are " + hiddenEdges + " - hidden edges, but no edges should ever be hidden!"); if (VizDebugUtils.isDebugFailMode()) { if (totalNodeCount_distinct != totalNodeCountTG_distinct) throw new OntopiaRuntimeException("The number of distinct nodes in" + " Vizigator (" + totalNodeCount_distinct + ") does not match the" + " number of distinct nodes in TouchGraph(" + totalNodeCountTG_distinct + ")."); } else { if (totalNodeCount_distinct != totalNodeCountTG_distinct) System.out.println("WARNING - The number of distinct nodes in" + " Vizigator (" + totalNodeCount_distinct + ") does not match the" + " number of distinct nodes in TouchGraph(" + totalNodeCountTG_distinct + ")."); } } protected Collection getTGNodesOfType(Class aClass, boolean distinct, boolean visible) { Collection nodesOfType = getTGNodesOfType(aClass, distinct ); ArrayList result = new ArrayList(nodesOfType.size()); for (Iterator iter = nodesOfType.iterator(); iter.hasNext();) { TMAbstractNode element = (TMAbstractNode) iter.next(); if (element.isVisible() == visible) result.add(element); } return result; } protected Collection getTGEdgesOfType(Class aClass, boolean distinct, boolean visible) { Collection edgesOfType = getTGEdgesOfType(aClass, distinct ); ArrayList result = new ArrayList(edgesOfType.size()); for (Iterator iter = edgesOfType.iterator(); iter.hasNext();) { Edge element = (Edge) iter.next(); if (element.isVisible() == visible) result.add(element); } return result; } protected Collection getTGEdgesOfType(Class aClass, boolean distinct) { Collection result; if (distinct) result = new HashSet(); else result = new ArrayList(); Iterator edgesIt = tgPanel.getAllEdges(); if (edgesIt != null) { while (edgesIt.hasNext()) { Object object = edgesIt.next(); if (aClass.isInstance(object)) result.add(object); } } return result; } protected Collection getTGNodesOfType(Class aClass, boolean distinct) { Collection result; if (distinct) result = new HashSet(); else result = new ArrayList(); Iterator nodesIt = tgPanel.getAllNodes(); if (nodesIt != null) { while (nodesIt.hasNext()) { Object object = nodesIt.next(); if (aClass.isInstance(object)) result.add(object); } } return result; } private void paintNodes(boolean visible) { Hashtable distances = new Hashtable(); if (getFocusNode() != null) { distances = GESUtils.calculateDistances((GraphEltSet) tgPanel.getGES(), getFocusNode(), 100); } for (Iterator topicByType = new ArrayList(nodesByType).iterator(); topicByType .hasNext();) { for (Iterator topics = (new ArrayList((List) topicByType.next())) .iterator(); topics.hasNext();) { TMTopicNode node = (TMTopicNode) topics.next(); if (node.isVisible() == visible) { Integer distance = (Integer) distances.get(node); char value = 'X'; if (distance != null) value = Character.forDigit(distance.intValue(), 10); node.paintSmallTag(tgPanel.getGraphics(), tgPanel, (int) node.drawx, (int) node.drawy, Color.black, Color.white, value); } } } } private void paintNodes() { Hashtable distances = new Hashtable(); if (getFocusNode() != null) { distances = GESUtils.calculateDistances((GraphEltSet) tgPanel.getGES(), getFocusNode(), 100); } for (Iterator topicByType = new ArrayList(nodesByType).iterator(); topicByType .hasNext();) { for (Iterator topics = (new ArrayList((List) topicByType.next())) .iterator(); topics.hasNext();) { TMTopicNode node = (TMTopicNode) topics.next(); Integer distance = (Integer) distances.get(node); char value = 'X'; if (distance != null) value = Character.forDigit(distance.intValue(), 10); node.paintSmallTag(tgPanel.getGraphics(), tgPanel, (int) node.drawx, (int) node.drawy, Color.black, Color.white, value); } } } public Collection getObjectsOfType(Class type, boolean distinct) { Collection result; if (distinct) result = new HashSet(); else result = new ArrayList(); for (Iterator collections = objectsByType.iterator(); collections .hasNext();) { for (Iterator objects = ((List) collections.next()).iterator(); objects .hasNext();) { Object object = objects.next(); if (type.isInstance(object)) result.add(object); } } return result; } public Collection getNodesOfType(Class type, boolean distinct) { Collection result; if (distinct) result = new HashSet(); else result = new ArrayList(); for (Iterator collections = nodesByType.iterator(); collections.hasNext();) { for (Iterator objects = ((List) collections.next()).iterator(); objects .hasNext();) { Object object = objects.next(); if (type.isInstance(object)) result.add(object); } } return result; } public void integrityCheck() { if (!VizDebugUtils.isDebugEnabled()) return; VizDebugUtils.debug("-------------- Start of Integrity check."); // Nodes in Vizigator Collection nodesViz = getNodesOfType(Node.class, false); Collection objectsViz = getObjectsOfType(Node.class, false); // Distinct nodes in Vizigator Collection nodesViz_distinct = getNodesOfType(Node.class, true); Collection objectsViz_distinct = getObjectsOfType(Node.class, true); // Nodes in TouchGraph Collection nodesTG = getTGNodesOfType(Node.class, false); // Distinct nodes in TouchGraph Collection nodesTG_distinct = getTGNodesOfType(Node.class, true); // Visible nodes in TouchGraph Collection visibleNodesTG = getTGNodesOfType(Node.class, true, true); // Hidden nodes in TouchGraph Collection hiddenNodesTG = getTGNodesOfType(Node.class, true, false); // Edges in Vizigator Collection edgesViz = getObjectsOfType(Edge.class, false); // Distinct edges in Vizigator Collection edgesViz_distinct = getObjectsOfType(Edge.class, true); // Edges in TouchGraph Collection edgesTG = getTGEdgesOfType(Edge.class, false); // Distinct edges in TouchGraph Collection edgesTG_distinct = getTGEdgesOfType(Edge.class, true); // Visible edges in TouchGraph Collection visibleEdgesTG = getTGEdgesOfType(Edge.class, true, true); // Hidden edges in TouchGraph Collection hiddenEdgesTG = getTGEdgesOfType(Edge.class, true, false); // Topics in Viz Collection topicsViz = getNodesOfType(TMTopicNode.class, false); Collection topicsViz_distinct = getNodesOfType(TMTopicNode.class, true); // Topics in TG Collection topicsTG = getTGNodesOfType(TMTopicNode.class, false); Collection topicsTG_distinct = getTGNodesOfType(TMTopicNode.class, true); // Associations in Viz Collection assocEdgesViz = getObjectsOfType(TMAssociationEdge.class, false); Collection assocNodesViz = getObjectsOfType(TMAssociationNode.class, false); // Distinct associations in Viz Collection assocEdgesViz_distinct = getObjectsOfType(TMAssociationEdge.class, true); Collection assocNodesViz_distinct = getObjectsOfType(TMAssociationNode.class, true); // Associations in TouchGraph Collection assocEdgesTG = getTGEdgesOfType(TMAssociationEdge.class, false); Collection assocNodesTG = getTGNodesOfType(TMAssociationNode.class, false); // Distinct associations in TouchGraph Collection assocEdgesTG_distinct = getTGEdgesOfType(TMAssociationEdge.class, true); Collection assocNodesTG_distinct = getTGNodesOfType(TMAssociationNode.class, true); // Roles in Viz Collection rolesViz = getObjectsOfType(TMRoleEdge.class, false); // Distinct roles in Viz Collection rolesViz_distinct = getObjectsOfType(TMRoleEdge.class, true); Collection rolesTG = getTGEdgesOfType(TMRoleEdge.class, false); Collection rolesTG_distinct = getTGEdgesOfType(TMRoleEdge.class, true); assertEmpty(hiddenNodesTG, "hidden nodes"); assertEmpty(hiddenEdgesTG, "hidden edges"); // nodesViz and nodesViz_distinct need not have the same size. // nodesViz just double counts nodes whose topics have multiple types. // assertSameSize(nodesViz, nodesViz_distinct, // "nodesViz", "nodesViz_distinct"); assertSameSize(objectsViz, objectsViz_distinct, "objectsViz", "objectsViz_distinct"); Collection allDistinctNodes = new ArrayList(nodesViz_distinct); allDistinctNodes.addAll(objectsViz_distinct); assertSameContents(allDistinctNodes, nodesTG, "allDistinctNodes", "nodesTG"); assertEqual(nodesViz_distinct.size() + objectsViz_distinct.size(), nodesTG.size(), "the number of distinct nodes in Vizigator", "the number of nodes in TouchGraph"); assertSameSize(nodesTG, nodesTG_distinct, "nodesTG", "nodesTG_distinct"); assertSameSize(nodesTG, visibleNodesTG, "nodesTG", "visibleNodesTG"); assertSameSize(edgesViz, edgesViz_distinct, "edgesViz", "edgesViz_distinct"); assertSameSize(edgesTG, edgesTG_distinct, "edgesTG", "edgesTG_distinct"); assertSameSize(edgesViz, edgesTG, "edgesViz", "edgesTG"); assertSameSize(edgesTG, visibleEdgesTG, "edgesTG", "visibleEdgesTG"); // topicsViz and topicsViz_distinct need not have the same size. // topicsViz just double counts nodes whose topics have multiple types. // assertSameSize(topicsViz, topicsViz_distinct, // "topicsViz", "topicsViz_distinct"); assertSameSize(topicsTG, topicsTG_distinct, "topicsTG", "topicsTG_distinct"); assertSameSize(topicsTG, topicsViz_distinct, "topicsTG", "topicsViz_distinct"); assertSameSize(assocEdgesViz, assocEdgesViz_distinct, "assocEdgesViz", "assocEdgesViz_distinct"); assertSameSize(assocEdgesTG, assocEdgesTG_distinct, "assocEdgesTG", "assocEdgesTG_distinct"); assertSameSize(assocEdgesViz, assocEdgesTG, "assocEdgesViz", "assocEdgesTG"); assertSameSize(assocNodesViz, assocNodesViz_distinct, "assocNodesViz", "assocNodesViz_distinct"); assertSameSize(assocNodesTG, assocNodesTG_distinct, "assocNodesTG", "assocNodesTG_distinct"); assertSameSize(assocNodesViz, assocNodesTG, "assocNodesViz", "assocNodesTG"); assertSameSize(rolesViz, rolesViz_distinct, "rolesViz", "rolesViz_distinct"); assertSameSize(rolesTG, rolesTG_distinct, "rolesViz", "rolesViz_distinct"); assertSameSize(rolesViz, rolesViz_distinct, "rolesViz", "rolesViz_distinct"); Collection allNodes = new HashSet(nodesViz); allNodes.addAll(objectsViz); assertSameContents(allNodes, nodesTG, "allNodes", "nodesTG"); VizDebugUtils.debug("-------------- End of Integrity check."); if (reportAndCrash) reportAndExit(); } private void assertEmpty(Collection coll, String whatsThere) { assertTrue(coll.isEmpty(), "WARNING! There are " + coll.size() + " " + whatsThere + "."); } private boolean assertSameSize(Collection coll1, Collection coll2, String name1, String name2) { return assertTrue(coll1.size() == coll2.size(), "WARNING! " + leadCap(name1) + " has size " + coll1.size() + ", but " + name2 + " has size " + coll2.size() + ". " + "They should have the same size."); } private boolean assertSameContents(Collection coll1, Collection coll2, String name1, String name2) { boolean retVal = assertSameSize(coll1, coll2, name1, name2); Iterator coll1It = coll1.iterator(); while (coll1It.hasNext()) { Object current = coll1It.next(); String label = current.toString(); if (current instanceof TMAbstractNode) { label = ((TMAbstractNode)current).getLabel(); } if (!assertTrue(coll2.contains(current), "The element " + label + " from the collection " + name1 + " could not be found in the collection " + name2 + ".")) retVal = false; } Iterator coll2It = coll2.iterator(); while (coll2It.hasNext()) { Object current = coll2It.next(); String label = current.toString(); if (current instanceof TMAbstractNode) { label = ((TMAbstractNode)current).getLabel(); } if (!assertTrue(coll1.contains(current), "The element " + label + " from the collection " + name2 + " could not be found in the collection " + name1 + ".")) retVal = false; } return retVal; } private boolean assertEqual(int number1, int number2, String name1, String name2) { return assertTrue(number1 == number2, "WARNING! " + leadCap(name1) + " is " + number1 + ", but " + name2 + " is " + number2 + ". " + "They should be equal."); } private boolean assertTrue(boolean asserted, String message) { if (!asserted) { VizDebugUtils.debug(message); reportAndCrash = true; } return asserted; } private void reportAndExit() { if (!VizDebugUtils.isDebugEnabled()) return; System.out.println("FOUND INCONSISTENCIES (SEE ABOVE)"); System.out.println("DISABLING VIZIGATOR USER"); vizigatorUser.enabled = false; // System.out.println("FOUND INCONSISTENCIES (SEE ABOVE)"); // System.out.println("EXITING VIZIGATOR"); // System.exit(1); } private boolean reportAndCrash = false; private String leadCap(String source) { String lead = source.substring(0, 1); String rest = source.substring(1); return lead.toUpperCase() + rest; } } /** * Sets all nodes to fixed (sticky) or not fixed. * @param fixed true(/false) if all nodes should get a (un)fixed position. */ public void setAllNodesFixed(boolean fixed) { Iterator nodesByTypeIt = nodesByType.iterator(); while (nodesByTypeIt.hasNext()) { Collection typeNodes = (Collection)nodesByTypeIt.next(); Iterator typeNodesIt = typeNodes.iterator(); while (typeNodesIt.hasNext()) { TMAbstractNode currentNode = (TMAbstractNode)typeNodesIt.next(); currentNode.setFixed(fixed); } } } public int getMaxTopicNameLength() { return maxTopicNameLength; } public void setMaxTopicNameLength(int length) { this.maxTopicNameLength = length; Iterator nodesByTypeIt = this.nodesByType.iterator(); while (nodesByTypeIt.hasNext()) { Collection nodesOfCurrentType = (Collection)nodesByTypeIt.next(); Iterator nodesOfCurrentTypeIt = nodesOfCurrentType.iterator(); while (nodesOfCurrentTypeIt.hasNext()) { TMTopicNode currentNode = (TMTopicNode)nodesOfCurrentTypeIt.next(); currentNode.updateLabel(); } } updateDisplayNoWork(); } }