Mercurial > hg > ccmieditor
view java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/DiagramModel.java @ 8:ea7885bd9bff tip
fixed bug : render solid line as dotted/dashed when moving the stylus from dotted/dashed to solid
author | ccmi-guest |
---|---|
date | Thu, 03 Jul 2014 16:12:20 +0100 |
parents | d66dd5880081 |
children |
line wrap: on
line source
/* CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package uk.ac.qmul.eecs.ccmi.diagrammodel; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreeNode; import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeProperties.Modifiers; import uk.ac.qmul.eecs.ccmi.utils.InteractionLog; /** * This class represent a model as per in the model-view control architecture. * The model is "double sided" in the sense that it can be accessed through either * a CollectionModel or a TreeModel returned by the respective getter methods. * The TreeModel is suitable for JTree classes of the swing library, while * the CollectionModel can be used by view classes by registering a CollectionListener * to the CollectionModel itself. * It is important to notice that changes made on one side will reflect on the other, * eventually triggering the registered listeners. * The tree model is structured according to a special layout which is suitable for * browsing the tree view via audio interface ( text to speech synthesis and sound). * * @param <N> a subclass of DiagramNode * @param <E> a subclass of DiagramEdge */ public class DiagramModel<N extends DiagramNode, E extends DiagramEdge>{ /** * Create a model instance starting from some nodes and edges prototypes. * All subsequently added element must be clones of such prototypes. * @param nodePrototypes an array of {@code DiagramNode} prototypes, from which * nodes that will be inserted in this model will be cloned * @param edgePrototypes an array of {@code DiagramEdge} prototypes, from which * edges that will be inserted in this model will be cloned */ @SuppressWarnings("serial") public DiagramModel(N [] nodePrototypes, E [] edgePrototypes) { root = new DiagramTreeNode(ROOT_LABEL){ @Override public boolean isRoot(){ return true; } }; modified = false; nodeCounter = 0; edgeCounter = 0; notifier = new ReentrantLockNotifier(); treeModel = new InnerTreeModel(root); treeModel.setEventSource(treeModel);/* default event source is the tree itself */ diagramCollection = new InnerDiagramCollection(); nodes = new ArrayList<N>(INITIAL_NODES_SIZE); edges = new ArrayList<E>(INITIAL_EDGES_SIZE); elements = new ArrayList<DiagramElement>(INITIAL_NODES_SIZE+INITIAL_EDGES_SIZE); changeListeners = new LinkedList<ChangeListener>(); for(N n : nodePrototypes) addType(n); for(E e : edgePrototypes){ addType(e); } } /** * Returns a CollectionModel for this diagram * * @return a CollectionModel for this diagram */ public CollectionModel<N,E> getDiagramCollection(){ return diagramCollection; } /** * Returns a TreeModel for this diagram * * @return a TreeModel for this diagram */ public TreeModel<N,E> getTreeModel(){ return treeModel; } private void handleChangeListeners(Object source){ if(modified) // fire the listener only the first time a change happens return; modified = true; fireChangeListeners(source); } private void addChangeListener(ChangeListener l){ changeListeners.add(l); } private void removeChangeListener(ChangeListener l){ changeListeners.remove(l); } protected void fireChangeListeners(Object source){ ChangeEvent changeEvent = new ChangeEvent(source); for(ChangeListener l : changeListeners) l.stateChanged(changeEvent); } private void addType(DiagramElement element){ DiagramTreeNode typeNode = _lookForChild(root, element.getType()); if(typeNode == null){ typeNode = new TypeMutableTreeNode(element); treeModel.insertNodeInto(typeNode, root, root.getChildCount()); } } private class InnerDiagramCollection implements CollectionModel<N,E> { public InnerDiagramCollection(){ listeners = new ArrayList<CollectionListener>(); } @Override public boolean insert(N n, Object source){ if(source == null) source = this; return _insert(n,source); } @Override public boolean insert(E e, Object source){ if(source == null) source = this; return _insert(e,source); } @Override public boolean takeOut(DiagramElement element, Object source){ if(source == null) source = this; if(element instanceof DiagramNode) return _takeOut((DiagramNode)element,source); if(element instanceof DiagramEdge) return _takeOut((DiagramEdge)element,source); return false; } @Override public void addCollectionListener(CollectionListener listener) { listeners.add(listener); } @Override public void removeCollectionListener(CollectionListener listener) { listeners.remove(listener); } protected void fireElementInserted(Object source, DiagramElement element) { for(CollectionListener l : listeners){ l.elementInserted(new CollectionEvent(source,element)); } } protected void fireElementTakenOut(Object source, DiagramElement element) { for(CollectionListener l : listeners){ l.elementTakenOut(new CollectionEvent(source,element)); } } protected void fireElementChanged(ElementChangedEvent evt){ for(CollectionListener l : listeners){ l.elementChanged(evt); } } @Override public Collection<N> getNodes() { return Collections.unmodifiableCollection(nodes); } @Override public Collection<E> getEdges() { return Collections.unmodifiableCollection(edges); } @Override public Collection<DiagramElement> getElements(){ return Collections.unmodifiableCollection(elements); } @Override public ReentrantLock getMonitor(){ return notifier; } @Override public void addChangeListener(ChangeListener l){ DiagramModel.this.addChangeListener(l); } @Override public void removeChangeListener(ChangeListener l){ DiagramModel.this.removeChangeListener(l); } /* sort the collections according to the id of nodes */ public void sort(){ Collections.sort(nodes, DiagramElementComparator.getInstance()); Collections.sort(edges, DiagramElementComparator.getInstance()); } public boolean isModified(){ return modified; } public void setUnmodified(){ modified = false; } protected ArrayList<CollectionListener> listeners; } @SuppressWarnings("serial") private class InnerTreeModel extends DefaultTreeModel implements TreeModel<N,E>{ public InnerTreeModel(TreeNode root){ super(root); bookmarks = new LinkedHashMap<String, DiagramTreeNode>(); diagramTreeNodeListeners = new ArrayList<DiagramTreeNodeListener>(); } @Override public boolean insertTreeNode(N treeNode, Object source){ if(source == null) source = this; return _insert(treeNode,source); } @Override public boolean insertTreeNode(E treeNode, Object source){ if(source == null) source = this; return _insert(treeNode,source); } @Override public boolean takeTreeNodeOut(DiagramElement treeNode, Object source){ if(source == null) source = this; boolean result; if(treeNode instanceof DiagramEdge){ result = _takeOut((DiagramEdge)treeNode,source); } else{ result = _takeOut((DiagramNode)treeNode,source); } /* remove the bookmarks associated with the just deleted diagram element, if any */ for(String key : treeNode.getBookmarkKeys()) bookmarks.remove(key); return result; } @Override public DiagramTreeNode putBookmark(String bookmark, DiagramTreeNode treeNode, Object source){ if(bookmark == null) throw new IllegalArgumentException("bookmark cannot be null"); if(source == null) source = this; setEventSource(source); treeNode.addBookmarkKey(bookmark); DiagramTreeNode result = bookmarks.put(bookmark, treeNode); nodeChanged(treeNode); iLog("bookmark added",bookmark); DiagramTreeNodeEvent evt = new DiagramTreeNodeEvent(treeNode,bookmark,source); for(DiagramTreeNodeListener l : diagramTreeNodeListeners){ l.bookmarkAdded(evt); } handleChangeListeners(this); return result; } @Override public DiagramTreeNode getBookmarkedTreeNode(String bookmark) { return bookmarks.get(bookmark); } @Override public DiagramTreeNode removeBookmark(String bookmark,Object source) { if(source == null) source = this; setEventSource(source); DiagramTreeNode treeNode = bookmarks.remove(bookmark); treeNode.removeBookmarkKey(bookmark); nodeChanged(treeNode); iLog("bookmark removed",bookmark); DiagramTreeNodeEvent evt = new DiagramTreeNodeEvent(treeNode,bookmark,source); for(DiagramTreeNodeListener l : diagramTreeNodeListeners){ l.bookmarkRemoved(evt); } handleChangeListeners(this); return treeNode; } @Override public Set<String> getBookmarks(){ return new LinkedHashSet<String>(bookmarks.keySet()); } @Override public void setNotes(DiagramTreeNode treeNode, String notes,Object source){ if(source == null) source = this; setEventSource(source); String oldValue = treeNode.getNotes(); treeNode.setNotes(notes,source); nodeChanged(treeNode); iLog("notes set for "+treeNode.getName(),"".equals(notes) ? "empty notes" : notes.replaceAll("\n", "\\\\n")); DiagramTreeNodeEvent evt = new DiagramTreeNodeEvent(treeNode,oldValue,source); for(DiagramTreeNodeListener l : diagramTreeNodeListeners){ l.notesChanged(evt); } handleChangeListeners(source); } private void setEventSource(Object source){ this.src = source; } @Override public ReentrantLock getMonitor(){ return notifier; } @Override public void addDiagramTreeNodeListener(DiagramTreeNodeListener l){ diagramTreeNodeListeners.add(l); } @Override public void removeDiagramTreeNodeListener(DiagramTreeNodeListener l){ diagramTreeNodeListeners.remove(l); } /* redefine the fire methods so that they set the source object according */ /* to whether the element was inserted from the graph or from the tree */ @Override protected void fireTreeNodesChanged(Object source, Object[] path, int[] childIndices, Object[] children) { super.fireTreeNodesChanged(src, path, childIndices, children); } @Override protected void fireTreeNodesInserted(Object source, Object[] path, int[] childIndices, Object[] children) { super.fireTreeNodesInserted(src, path, childIndices, children); } @Override protected void fireTreeNodesRemoved(Object source, Object[] path, int[] childIndices, Object[] children) { super.fireTreeNodesRemoved(src, path, childIndices, children); } @Override protected void fireTreeStructureChanged(Object source, Object[] path, int[] childIndices, Object[] children) { super.fireTreeStructureChanged(src, path, childIndices, children); } public boolean isModified(){ return modified; } public void setUnmodified(){ modified = false; } private Object src; private Map<String, DiagramTreeNode> bookmarks; private ArrayList<DiagramTreeNodeListener> diagramTreeNodeListeners; } @SuppressWarnings("serial") class ReentrantLockNotifier extends ReentrantLock implements ElementNotifier { @Override public void notifyChange(ElementChangedEvent evt) { _change(evt); handleChangeListeners(evt.getDiagramElement()); } } private boolean _insert(N n, Object source) { assert(n != null); /* if id has already been given then sync the counter so that a surely new value is given to the next nodes */ if(n.getId() == DiagramElement.NO_ID) n.setId(++nodeCounter); else if(n.getId() > nodeCounter) nodeCounter = n.getId(); treeModel.setEventSource(source); nodes.add(n); elements.add(n); /* add the node to outer node's (if any) inner nodes */ if(n.getExternalNode() != null) n.getExternalNode().addInternalNode(n); /* decide where to insert the node based on whether this is an inner node or not */ MutableTreeNode parent; if(n.getExternalNode() == null){ DiagramTreeNode typeNode = _lookForChild(root, n.getType()); if(typeNode == null) throw new IllegalArgumentException("Node type "+n.getType()+" not present in the model"); parent = typeNode; }else{ parent = n.getExternalNode(); } /* add to the node one child per property type */ for(String propertyType : n.getProperties().getTypes()) n.insert(new PropertyTypeMutableTreeNode(propertyType,n), n.getChildCount()); /* inject the notifier for managing changes internal to the edge */ n.setNotifier(notifier); /* insert node into tree which fires tree listeners */ treeModel.insertNodeInto(n, parent, parent.getChildCount()); /* this is necessary to increment the child counter displayed between brackets */ treeModel.nodeChanged(parent); diagramCollection.fireElementInserted(source,n); handleChangeListeners(n); iLog("node inserted",DiagramElement.toLogString(n)); return true; } private boolean _takeOut(DiagramNode n, Object source) { treeModel.setEventSource(source); /* recursively remove internal nodes of this node */ _removeInternalNodes(n,source); /* clear external node and clear edges attached to this node and updates other ends of such edges */ _clearNodeReferences(n,source); /* remove the node from the tree (fires listeners) */ treeModel.removeNodeFromParent(n); /* this is necessary to increment the child counter displayed between brackets */ treeModel.nodeChanged(n.getParent()); /* remove the nodes from the collection */ nodes.remove(n); elements.remove(n); /* notify all the listeners a new node has been removed */ diagramCollection.fireElementTakenOut(source,n); handleChangeListeners(n); if(nodes.isEmpty()){ nodeCounter = 0; }else{ long lastNodeId = nodes.get(nodes.size()-1).getId(); if(n.getId() > lastNodeId) nodeCounter = lastNodeId; } iLog("node removed",DiagramElement.toLogString(n)); return true; } private boolean _insert(E e, Object source) { assert(e != null); /* executes formal controls over the edge's node, which must be specified from the outer class*/ if(e.getNodesNum() < 2) throw new MalformedEdgeException("too few (" +e.getNodesNum()+ ") nodes"); /* if id has already been given then sync the counter so that a surely new value is given to the next edges */ if(e.getId() > edgeCounter) edgeCounter = e.getId(); else e.setId(++edgeCounter); treeModel.setEventSource(source); edges.add(e); elements.add(e); /* updates the nodes' edge reference and the edge tree references */ for(int i = e.getNodesNum()-1; i >= 0; i--){ DiagramNode n = e.getNodeAt(i); assert(n != null); /* insert first the type of the edge, if not already present */ DiagramTreeNode edgeType = _lookForChild(n, e.getType()); if(edgeType == null){ edgeType = new EdgeReferenceHolderMutableTreeNode(e.getType()); treeModel.insertNodeInto(edgeType, n, 0); } /* insert the edge reference under its type tree node, in the node*/ treeModel.insertNodeInto(new EdgeReferenceMutableTreeNode(e,n), edgeType, 0); /* this is necessary to increment the child counter displayed between brackets */ treeModel.nodeChanged(edgeType); n.addEdge(e); /* insert the node reference into the edge tree node */ e.insert(new NodeReferenceMutableTreeNode(n,e), 0); } DiagramTreeNode parent = _lookForChild(root, e.getType()); if(parent == null) throw new IllegalArgumentException("Edge type "+e.getType()+" not present in the model"); /* inject the controller and notifier to manage changes internal to the edge */ e.setNotifier(notifier); /* c'mon baby light my fire */ treeModel.insertNodeInto(e, parent, parent.getChildCount()); /* this is necessary to increment the child counter displayed between brackets */ treeModel.nodeChanged(parent); diagramCollection.fireElementInserted(source,e); handleChangeListeners(e); StringBuilder builder = new StringBuilder(DiagramElement.toLogString(e)); builder.append(" connecting:"); for(int i=0; i<e.getNodesNum();i++) builder.append(DiagramElement.toLogString(e.getNodeAt(i))).append(' '); iLog("edge inserted",builder.toString()); return true; } private boolean _takeOut(DiagramEdge e, Object source) { treeModel.setEventSource(source); /* update the nodes attached to this edge */ _clearEdgeReferences(e); /* remove the edge from the collection */ edges.remove(e); elements.remove(e); /* remove the edge from the tree (fires tree listeners) */ treeModel.removeNodeFromParent(e); /* this is necessary to increment the child counter displayed between brackets */ treeModel.nodeChanged(e.getParent()); /* notify listeners for collection */ diagramCollection.fireElementTakenOut(source,e); handleChangeListeners(e); if(edges.isEmpty()){ edgeCounter = 0; }else{ long lastEdgeId = edges.get(edges.size()-1).getId(); if(e.getId() > lastEdgeId) edgeCounter = lastEdgeId; } iLog("edge removed",DiagramElement.toLogString(e)); return true; } private void _removeInternalNodes(DiagramNode n, Object source){ for(int i=0; i<n.getInternalNodesNum(); i++){ DiagramNode innerNode = n.getInternalNodeAt(i); _clearNodeReferences(innerNode, source); _removeInternalNodes(innerNode, source); n.removeInternalNode(innerNode); nodes.remove(n); } } /* removes both inner and tree node references to an edge from nodes it's attached to */ private void _clearEdgeReferences(DiagramEdge e){ for(int i=0; i<e.getNodesNum();i++){ DiagramNode n = e.getNodeAt(i); EdgeReferenceMutableTreeNode reference; /* find the category tree node under which our reference is */ reference = _lookForEdgeReference(n, e); assert(reference != null); treeModel.removeNodeFromParent(reference); DiagramTreeNode type = _lookForChild(n, e.getType()); if(type.isLeaf()) treeModel.removeNodeFromParent(type); n.removeEdge(e); } } /* removes references from node */ private void _clearNodeReferences(DiagramNode n, Object source){ /* remove the node itself from its external node, if any */ if(n.getExternalNode() != null) n.getExternalNode().removeInternalNode(n); /* remove edges attached to this node from the collection */ ArrayList<DiagramEdge> edgesToRemove = new ArrayList<DiagramEdge>(edges.size()); for(int i=0; i<n.getEdgesNum(); i++){ DiagramEdge e = n.getEdgeAt(i); if(e.getNodesNum() == 2){ // deleting a node on a two ends edge means deleting the edge itself edgesToRemove.add(e); }else{ e.removeNode(n,source); DiagramTreeNode nodeTreeReference = _lookForNodeReference(e, n); treeModel.removeNodeFromParent(nodeTreeReference); } } /* remove the edges that must no longer exist as were two ended edges attached to this node */ for(DiagramEdge e : edgesToRemove) _takeOut(e, source); } /* the tree structure is changed as a consequence of this method, therefore it's synchronized * * so that external classes accessing the tree can get exclusive access through getMonitor() */ private void _change(ElementChangedEvent evt){ synchronized(this){ String changeType = evt.getChangeType(); /* don't use the event source as it might collide with other threads as * changes on the collections and inner changes on the node are synch'ed thought different monitors */ /* treeModel.setEventSource(evt.getSource());*/ if("name".equals(changeType)){ if(evt.getDiagramElement() instanceof DiagramNode){ DiagramNode n = (DiagramNode)evt.getDiagramElement(); for(int i=0; i<n.getEdgesNum(); i++){ DiagramEdge e = n.getEdgeAt(i); treeModel.nodeChanged(_lookForNodeReference(e,n)); treeModel.nodeChanged(_lookForEdgeReference(n,e)); for(int j=0; j<e.getNodesNum(); j++){ DiagramNode n2 = e.getNodeAt(j); if(n2 != n) treeModel.nodeChanged(_lookForEdgeReference(n2,e)); } } iLog("node name changed",DiagramElement.toLogString(n)); }else{ DiagramEdge e = (DiagramEdge)evt.getDiagramElement(); for(int i=0; i<e.getNodesNum();i++){ DiagramNode n = e.getNodeAt(i); treeModel.nodeChanged(_lookForEdgeReference(n,e)); } iLog("edge name changed",DiagramElement.toLogString(e)); } treeModel.nodeChanged(evt.getDiagramElement()); }else if("properties".equals(changeType)){ DiagramNode n = (DiagramNode)evt.getDiagramElement(); for(String type : n.getProperties().getTypes()){ PropertyTypeMutableTreeNode typeNode = null; for(int i=0; i<n.getChildCount();i++){ /* find the child treeNode corresponding to the current type */ if(n.getChildAt(i) instanceof PropertyTypeMutableTreeNode) if(type.equals(((PropertyTypeMutableTreeNode)n.getChildAt(i)).getType())){ typeNode = (PropertyTypeMutableTreeNode)n.getChildAt(i); break; } } if(typeNode == null) throw new IllegalArgumentException("Inserted Node property type "+ type + " not present in the tree" ); /* set the name and modifier string of all the children PropertyNodes */ typeNode.setValues(n.getProperties().getValues(type), n.getProperties().getModifiers(type)); } treeModel.nodeStructureChanged(evt.getDiagramElement()); iLog("node properties changed",n.getProperties().toString()); }else if("properties.clear".equals(changeType)){ DiagramNode n = (DiagramNode)evt.getDiagramElement(); List<String> empty = Collections.emptyList(); for(int i=0; i<n.getChildCount();i++){ /* find the child treeNode corresponding to the current type */ if(n.getChildAt(i) instanceof PropertyTypeMutableTreeNode){ ((PropertyTypeMutableTreeNode)n.getChildAt(i)).setValues(empty, null); } } treeModel.nodeStructureChanged(evt.getDiagramElement()); }else if("property.add".equals(changeType)){ DiagramNode n = (DiagramNode)evt.getDiagramElement(); ElementChangedEvent.PropertyChangeArgs args = (ElementChangedEvent.PropertyChangeArgs)evt.getArguments(); PropertyTypeMutableTreeNode typeNode = (PropertyTypeMutableTreeNode)_lookForChild(n,args.getPropertyType()); PropertyMutableTreeNode propertyNode = new PropertyMutableTreeNode(n.getProperties().getValues(args.getPropertyType()).get(args.getPropertyIndex())); typeNode.add(propertyNode); treeModel.insertNodeInto(propertyNode, typeNode, args.getPropertyIndex()); /* this is necessary to increment the child counter displayed between brackets */ treeModel.nodeChanged(typeNode); iLog("property inserted",propertyNode.getName()); }else if("property.set".equals(changeType)){ DiagramNode n = (DiagramNode)evt.getDiagramElement(); ElementChangedEvent.PropertyChangeArgs args = (ElementChangedEvent.PropertyChangeArgs)evt.getArguments(); PropertyTypeMutableTreeNode typeNode = (PropertyTypeMutableTreeNode)_lookForChild(n,args.getPropertyType()); ((DiagramTreeNode)typeNode.getChildAt(args.getPropertyIndex())) .setUserObject(n.getProperties().getValues(args.getPropertyType()).get(args.getPropertyIndex())); treeModel.nodeChanged((typeNode.getChildAt(args.getPropertyIndex()))); iLog("property changed",n.getProperties().getValues(args.getPropertyType()).get(args.getPropertyIndex())); }else if("property.remove".equals(changeType)){ DiagramNode n = (DiagramNode)evt.getDiagramElement(); ElementChangedEvent.PropertyChangeArgs args = (ElementChangedEvent.PropertyChangeArgs)evt.getArguments(); PropertyTypeMutableTreeNode typeNode = (PropertyTypeMutableTreeNode)_lookForChild(n,args.getPropertyType()); iLog("property removed",((DiagramTreeNode)typeNode.getChildAt(args.getPropertyIndex())).getName()); //must do it before actual removing treeModel.removeNodeFromParent((DiagramTreeNode)typeNode.getChildAt(args.getPropertyIndex())); /* remove the bookmark keys associated with this property tree node, if any */ for(String key : treeModel.getBookmarks()){ treeModel.bookmarks.remove(key); } }else if("property.modifiers".equals(changeType)){ DiagramNode n = (DiagramNode)evt.getDiagramElement(); ElementChangedEvent.PropertyChangeArgs args = (ElementChangedEvent.PropertyChangeArgs)evt.getArguments(); PropertyTypeMutableTreeNode typeNode = (PropertyTypeMutableTreeNode)_lookForChild(n,args.getPropertyType()); PropertyMutableTreeNode propertyNode = ((PropertyMutableTreeNode)typeNode.getChildAt(args.getPropertyIndex())); StringBuilder builder = new StringBuilder(); Modifiers modifiers = n.getProperties().getModifiers(args.getPropertyType()); for(int index : modifiers.getIndexes(args.getPropertyIndex())) builder.append(modifiers.getTypes().get(index)).append(' '); propertyNode.setModifierString(builder.toString()); treeModel.nodeChanged(propertyNode); }else if("arrowHead".equals(changeType)||"endLabel".equals(changeType)){ /* source is considered to be the node whose end of the edge was changed */ DiagramNode source = (DiagramNode)evt.getArguments(); DiagramEdge e = (DiagramEdge)evt.getDiagramElement(); treeModel.nodeChanged(e); for(int i=0; i<e.getChildCount();i++){ NodeReferenceMutableTreeNode ref = (NodeReferenceMutableTreeNode)e.getChildAt(i); if(ref.getNode() == source){ treeModel.nodeChanged(ref); iLog(("arrowHead".equals(changeType) ? "arrow head changed" :"end label changed"), "edge:"+DiagramElement.toLogString(e)+" node:"+DiagramElement.toLogString(ref.getNode())+ " value:"+ ("arrowHead".equals(changeType) ? e.getEndDescription(ref.getNode()): e.getEndLabel(ref.getNode()))); break; } } }else if("notes".equals(changeType)){ /* do nothing as the tree update is taken care in the tree itself * and cannot do it here because it must work for all the diagram tree nodes */ } } /* do nothing for other ElementChangedEvents as they only concern the diagram listeners */ /* just forward the event to other listeners which might have been registered */ diagramCollection.fireElementChanged(evt); } private static DiagramTreeNode _lookForChild(DiagramTreeNode parentNode, String name){ DiagramTreeNode child = null, temp; for(@SuppressWarnings("unchecked") Enumeration<DiagramTreeNode> children = parentNode.children(); children.hasMoreElements();){ temp = children.nextElement(); if(temp.getName().equals(name)){ child = temp; break; } } return child; } private static NodeReferenceMutableTreeNode _lookForNodeReference(DiagramEdge parent, DiagramNode n){ NodeReferenceMutableTreeNode child = null, temp; for(@SuppressWarnings("unchecked") Enumeration<DiagramTreeNode> children = parent.children(); children.hasMoreElements();){ temp = (NodeReferenceMutableTreeNode)children.nextElement(); if( ((NodeReferenceMutableTreeNode)temp).getNode().equals(n)){ child = temp; break; } } return child; } private static EdgeReferenceMutableTreeNode _lookForEdgeReference( DiagramNode parentNode, DiagramEdge e){ DiagramTreeNode edgeType = _lookForChild(parentNode, e.getType()); assert(edgeType != null); EdgeReferenceMutableTreeNode child = null, temp; for(@SuppressWarnings("unchecked") Enumeration<DiagramTreeNode> children = edgeType.children(); children.hasMoreElements();){ temp = (EdgeReferenceMutableTreeNode)children.nextElement(); if( ((EdgeReferenceMutableTreeNode)temp).getEdge().equals(e)){ child = temp; break; } } return child; } private void iLog(String action,String args){ InteractionLog.log("MODEL",action,args); } private DiagramTreeNode root; private InnerDiagramCollection diagramCollection; private ArrayList<N> nodes; private ArrayList<E> edges; private ArrayList<DiagramElement> elements; private InnerTreeModel treeModel; private long edgeCounter; private long nodeCounter; private ReentrantLockNotifier notifier; private List<ChangeListener> changeListeners; private boolean modified; private final static String ROOT_LABEL = "Diagram"; private final static int INITIAL_EDGES_SIZE = 20; private final static int INITIAL_NODES_SIZE = 30;}