package org.simantics.g3d.scl;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
*
* @param <E>
*/
-public abstract class ScriptNodeMap<E extends INode> implements NodeMap<Object,E>, IMappingListener, NodeListener {
+public abstract class ScriptNodeMap<DBObject,E extends INode> implements NodeMap<DBObject,Object,E>, IMappingListener, NodeListener {
- private static final boolean DEBUG = false;
-
- protected Session session;
- protected IMapping<Object,E> mapping;
-
- protected ParentNode<E> rootNode;
-
- protected Set<E> nodes = new HashSet<E>();
-
- private boolean dirty = false;
-
- public ScriptNodeMap(Session session, IMapping<Object,E> mapping, ParentNode<E> rootNode) {
- this.session = session;
- this.mapping = mapping;
- this.rootNode = rootNode;
- mapping.addMappingListener(this);
- rootNode.addListener(this);
- }
-
-
-
- protected abstract void addActor(E node);
- protected abstract void removeActor(E node);
- protected abstract void updateActor(E node,Set<String> ids);
-
- public void repaint() {
- dirty = true;
- }
-
- public void populate() {
- for (E node : rootNode.getNodes()) {
- receiveAdd(node, node.getParentRel(),true);
- }
- repaint();
- }
-
- @Override
- public E getNode(Object o) {
- return null;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public Collection<Object> getRenderObjects(INode node) {
- return Collections.EMPTY_LIST;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public ParentNode<E> getRootNode() {
- return (ParentNode<E>)rootNode;
- }
-
-
-
- @Override
- public boolean isChangeTracking() {
- return changeTracking;
- }
-
- @Override
- public void setChangeTracking(boolean enabled) {
- changeTracking = enabled;
- }
-
- private boolean changeTracking = true;
-
- protected Object syncMutex = new Object();
-
+ private static final boolean DEBUG = false;
+
+ protected Session session;
+ protected IMapping<DBObject,INode> mapping;
+
+ protected ParentNode<E> rootNode;
+
+ protected Set<E> nodes = new HashSet<E>();
+
+ private boolean dirty = false;
+
+ public ScriptNodeMap(Session session, IMapping<DBObject,INode> mapping, ParentNode<E> rootNode) {
+ this.session = session;
+ this.mapping = mapping;
+ this.rootNode = rootNode;
+ mapping.addMappingListener(this);
+ rootNode.addListener(this);
+ }
+
+
+
+ protected abstract void addActor(E node);
+ protected abstract void removeActor(E node);
+ protected abstract void updateActor(E node,Set<String> ids);
+
+ public void repaint() {
+ dirty = true;
+ }
+
+ public void populate() {
+ for (E node : rootNode.getNodes()) {
+ receiveAdd(node, node.getParentRel(),true);
+ }
+ repaint();
+ }
+
+ @Override
+ public E getNode(Object o) {
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Collection<Object> getRenderObjects(INode node) {
+ return Collections.EMPTY_LIST;
+ }
+
+ @Override
+ public ParentNode<E> getRootNode() {
+ return rootNode;
+ }
+
+
+
+ @Override
+ public boolean isChangeTracking() {
+ return changeTracking;
+ }
+
+ @Override
+ public void setChangeTracking(boolean enabled) {
+ changeTracking = enabled;
+ }
+
+ private boolean changeTracking = true;
+
+ protected Object syncMutex = new Object();
+
- private List<Pair<E,String>> added = new ArrayList<Pair<E,String>>();
- private List<Pair<E,String>> removed = new ArrayList<Pair<E,String>>();
- private MapSet<E, String> updated = new MapSet.Hash<E, String>();
+ private List<Pair<E,String>> added = new ArrayList<Pair<E,String>>();
+ private List<Pair<E,String>> removed = new ArrayList<Pair<E,String>>();
+ private MapSet<E, String> updated = new MapSet.Hash<E, String>();
- private boolean rangeModified = false;
-
-
-
- @SuppressWarnings("unchecked")
- @Override
- public void updateRenderObjectsFor(E node) {
- nodes.add((E)node);
-
- }
-
- @SuppressWarnings("unchecked")
- private void receiveAdd(E node, String id, boolean db) {
- if (DEBUG) System.out.println("receiveAdd " + debugString(node) + " " + id + " " + db);
- synchronized (syncMutex) {
- for (Pair<E, String> n : added) {
- if (n.first.equals(node))
- return;
- }
- if (changeTracking) {
- mapping.rangeModified((E)node.getParent());
- }
- added.add(new Pair<E, String>(node, id));
- rangeModified = true;
- }
- repaint();
- }
-
- @SuppressWarnings("unchecked")
- private void receiveRemove(E node, String id, boolean db) {
- if (DEBUG) System.out.println("receiveRemove " + debugString(node) + " " + id + " " + db);
- synchronized (syncMutex) {
- for (Pair<E, String> n : removed) {
- if (n.first.equals(node))
- return;
- }
- if (changeTracking && !db)
- mapping.rangeModified((E)node.getParent());
- removed.add(new Pair<E, String>(node, id));
- rangeModified = true;
- }
- repaint();
- }
-
- @SuppressWarnings("unchecked")
- private void receiveUpdate(E node, String id, boolean db) {
- if (DEBUG) System.out.println("receiveUpdate " + debugString(node) + " " + id + " " + db);
- synchronized (syncMutex) {
+ private boolean rangeModified = false;
+
+ @Override
+ public void updateRenderObjectsFor(E node) {
+ nodes.add(node);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void receiveAdd(E node, String id, boolean db) {
+ if (DEBUG) System.out.println("receiveAdd " + debugString(node) + " " + id + " " + db);
+ synchronized (syncMutex) {
+ for (Pair<E, String> n : added) {
+ if (n.first.equals(node))
+ return;
+ }
+ if (changeTracking) {
+ mapping.rangeModified((E)node.getParent());
+ }
+ added.add(new Pair<E, String>(node, id));
+ rangeModified = true;
+ }
+ repaint();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void receiveRemove(E node, String id, boolean db) {
+ if (DEBUG) System.out.println("receiveRemove " + debugString(node) + " " + id + " " + db);
+ synchronized (syncMutex) {
+ for (Pair<E, String> n : removed) {
+ if (n.first.equals(node))
+ return;
+ }
+ if (changeTracking && !db)
+ mapping.rangeModified((E)node.getParent());
+ removed.add(new Pair<E, String>(node, id));
+ rangeModified = true;
+ }
+ repaint();
+ }
+
+ private void receiveUpdate(E node, String id, boolean db) {
+ if (DEBUG) System.out.println("receiveUpdate " + debugString(node) + " " + id + " " + db);
+ synchronized (syncMutex) {
// for (Pair<E, String> n : updated) {
// if (n.first.equals(node))
// return;
// }
- if (changeTracking && !db)
- mapping.rangeModified(node);
- //updated.add(new Pair<E, String>(node, id));
- updated.add(node, id);
- rangeModified = true;
- }
- repaint();
- }
-
- private boolean graphUpdates = false;
- private Set<E> graphModified = new HashSet<E>();
-
- private boolean requestCommit = false;
- private String commitMessage = null;
-
- @Override
- public void commit(String message) {
- requestCommit = true;
- commitMessage = message;
- }
-
- protected void doCommit() throws DatabaseException {
- session.syncRequest(new WriteRequest() {
-
- @Override
- public void perform(WriteGraph graph) throws DatabaseException {
- if (DEBUG) System.out.println("Commit " + commitMessage);
- if (commitMessage != null) {
- Layer0Utils.addCommentMetadata(graph, commitMessage);
- graph.markUndoPoint();
- commitMessage = null;
- }
- commit(graph);
- }
-
- });
- }
-
- protected void commit(WriteGraph graph) throws DatabaseException {
- synchronized(syncMutex) {
- if (DEBUG) System.out.println("Commit");
- graphUpdates = true;
- mapping.updateDomain(graph);
- graphUpdates = false;
- clearDeletes();
- if (DEBUG) System.out.println("Commit done");
- }
- }
-
-
-
- @Override
- public void domainModified() {
- if (graphUpdates)
- return;
- if (DEBUG)System.out.println("domainModified");
- // FIXME : this is called by IMapping id DB thread
- session.asyncRequest(new ReadRequest() {
-
- @SuppressWarnings("unchecked")
- @Override
- public void run(ReadGraph graph) throws DatabaseException {
- update(graph);
- }
- });
-
- }
-
- protected void reset(ReadGraph graph) throws MappingException {
- if (DEBUG) System.out.println("Reset");
- synchronized (syncMutex) {
- graphUpdates = true;
- mapping.getRangeModified().clear();
- for (Object o : mapping.getDomain())
- mapping.domainModified(o);
- mapping.updateRange(graph);
- graphModified.clear();
- graphUpdates = false;
- }
- }
-
-
- protected void update(ReadGraph graph) throws DatabaseException {
- if (DEBUG) System.out.println("Graph update start");
- synchronized (syncMutex) {
- graphUpdates = true;
- for (Object domainObject : mapping.getDomainModified()) {
- E rangeObject = mapping.get(domainObject);
- if (rangeObject != null)
- graphModified.add(rangeObject);
- }
- mapping.updateRange(graph);
- graphModified.clear();
- syncDeletes();
- clearDeletes();
- graphUpdates = false;
- }
-
-
- //if (mapping.isRangeModified() && !runUndo) // FIXME : redo?
- if (mapping.isRangeModified())
- commit((String)null);
- if (DEBUG) System.out.println("Graph update done");
- }
-
- @Override
- public void rangeModified() {
- //System.out.println("rangeModified");
+ if (changeTracking && !db)
+ mapping.rangeModified(node);
+ //updated.add(new Pair<E, String>(node, id));
+ updated.add(node, id);
+ rangeModified = true;
+ }
+ repaint();
+ }
+
+ private boolean graphUpdates = false;
+ private Set<E> graphModified = new HashSet<E>();
+
+ private boolean requestCommit = false;
+ private String commitMessage = null;
+
+ @Override
+ public void commit(String message) {
+ requestCommit = true;
+ commitMessage = message;
+ }
+
+ protected void doCommit() throws DatabaseException {
+ session.syncRequest(new WriteRequest() {
+
+ @Override
+ public void perform(WriteGraph graph) throws DatabaseException {
+ if (DEBUG) System.out.println("Commit " + commitMessage);
+ if (commitMessage != null) {
+ Layer0Utils.addCommentMetadata(graph, commitMessage);
+ graph.markUndoPoint();
+ commitMessage = null;
+ }
+ commit(graph);
+ }
+
+ });
+ }
+
+ protected void commit(WriteGraph graph) throws DatabaseException {
+ synchronized(syncMutex) {
+ if (DEBUG) System.out.println("Commit");
+ graphUpdates = true;
+ mapping.updateDomain(graph);
+ graphUpdates = false;
+ clearDeletes();
+ if (DEBUG) System.out.println("Commit done");
+ }
+ }
+
+
+
+ @Override
+ public void domainModified() {
+ if (graphUpdates)
+ return;
+ if (DEBUG)System.out.println("domainModified");
+ // FIXME : this is called by IMapping id DB thread
+ dirty = true;
+// session.asyncRequest(new ReadRequest() {
+//
+// @SuppressWarnings("unchecked")
+// @Override
+// public void run(ReadGraph graph) throws DatabaseException {
+// update(graph);
+// }
+// });
+
+ }
+
+ protected void reset(ReadGraph graph) throws MappingException {
+ if (DEBUG) System.out.println("Reset");
+ synchronized (syncMutex) {
+ graphUpdates = true;
+ mapping.getRangeModified().clear();
+ for (DBObject o : mapping.getDomain())
+ mapping.domainModified(o);
+ mapping.updateRange(graph);
+ graphModified.clear();
+ graphUpdates = false;
+ }
+ }
+
+ protected void update(ReadGraph graph) throws DatabaseException {
+ if (DEBUG) System.out.println("Graph update start");
+ synchronized (syncMutex) {
+ graphUpdates = true;
+ for (DBObject domainObject : mapping.getDomainModified()) {
+ @SuppressWarnings("unchecked")
+ E rangeObject = (E) mapping.get(domainObject);
+ if (rangeObject != null)
+ graphModified.add(rangeObject);
+ }
+ mapping.updateRange(graph);
+ graphModified.clear();
+ syncDeletes();
+ clearDeletes();
+ graphUpdates = false;
+ }
+
+
+ //if (mapping.isRangeModified() && !runUndo) // FIXME : redo?
+ if (mapping.isRangeModified())
+ commit((String)null);
+ if (DEBUG) System.out.println("Graph update done");
+ }
+
+ @Override
+ public void rangeModified() {
+ //System.out.println("rangeModified");
- }
-
- public void update() throws DatabaseException{
- while (dirty) {
- dirty = false;
- // preRender
- updateCycle();
- // postRender
- if (requestCommit && !rangeModified) { // FIXME : not thread safe.
- requestCommit = false;
- doCommit();
- }
- }
- }
-
+ }
+
+ public void update() throws DatabaseException{
+ while (dirty) {
+ dirty = false;
+ // preRender
+ updateCycle();
+ // postRender
+ if (requestCommit && !rangeModified) { // FIXME : not thread safe.
+ requestCommit = false;
+ doCommit();
+ }
+ session.syncRequest(new ReadRequest() {
+ @Override
+ public void run(ReadGraph graph) throws DatabaseException {
+ update(graph);
+ }
+ });
+ }
+ }
+
-
- // Reusable containers for data synchronisation
- List<Pair<E, String>> rem = new ArrayList<Pair<E,String>>(); // Removed objects
- List<Pair<E, String>> add = new ArrayList<Pair<E,String>>(); // Added objects
- MapSet<E, String> mod = new MapSet.Hash<E, String>(); // Modified objects
- Set<E> propagation = new HashSet<E>(); // Objects with propagated changes
- Stack<E> stack = new Stack<E>(); // Stack for handling propagation
- Set<E> delete = Collections.synchronizedSet(new HashSet<E>()); // Objects to be completely deleted
- Set<E> deleteUC = new HashSet<E>();
-
-
-
-
- /**
- * When objects are removed (either from Java or Graph), after remove processing the Java objects remain in mapping cache.
- * This causes problems with Undo and Redo, whcih the end up re-using the removed objects from mapping cache.
- *
- * This code here synchronizes removed and added objects to collect deletable objects. (a deletable object is one which is removed but not added).
- *
- */
- protected void syncDeletes() {
- deleteUC.clear();
- for (Pair<E, String> n : removed) {
- deleteUC.add(n.first);
- }
- for (Pair<E, String> n : added) {
- deleteUC.remove(n.first);
- }
- if (DEBUG && deleteUC.size() > 0) {
- System.out.println("Delete sync");
- for (E n : delete) {
- System.out.println(debugString(n));
- }
- }
- delete.addAll(deleteUC);
- deleteUC.clear();
- }
-
- /**
- * Clears deletable objects from mapping cache.
- */
- protected void clearDeletes() {
- if (DEBUG && delete.size() > 0) System.out.println("Delete");
- for (E n : delete) {
- if (DEBUG) System.out.println(debugString(n));
- mapping.getRange().remove(n);
- }
- delete.clear();
- }
-
- protected String debugString(E n) {
- return n + "@" + Integer.toHexString(n.hashCode());
- }
-
- @SuppressWarnings("unchecked")
- protected void updateCycle() {
- rem.clear();
- add.clear();
- mod.clear();
- propagation.clear();
-
-
- synchronized (syncMutex) {
- rem.addAll(removed);
- add.addAll(added);
- for (E e : updated.getKeys()) {
- for (String s : updated.getValues(e)) {
- mod.add(e, s);
- }
- }
- syncDeletes();
- removed.clear();
- added.clear();
- updated.clear();
- }
-
- for (Pair<E, String> n : rem) {
- stopListening(n.first);
- removeActor(n.first);
- }
-
- for (Pair<E, String> n : add) {
- addActor(n.first);
- listen(n.first);
- }
-
- for (E e : mod.getKeys()) {
- Set<String> ids = mod.getValues(e);
- if (ids.contains(G3D.URIs.hasPosition) || ids.contains(G3D.URIs.hasOrientation)) {
- if (!propagation.contains(e))
- propagation.add(e);
+
+ // Reusable containers for data synchronisation
+ List<Pair<E, String>> rem = new ArrayList<Pair<E,String>>(); // Removed objects
+ List<Pair<E, String>> add = new ArrayList<Pair<E,String>>(); // Added objects
+ MapSet<E, String> mod = new MapSet.Hash<E, String>(); // Modified objects
+ Set<E> propagation = new HashSet<E>(); // Objects with propagated changes
+ Stack<E> stack = new Stack<E>(); // Stack for handling propagation
+ Set<E> delete = Collections.synchronizedSet(new HashSet<E>()); // Objects to be completely deleted
+ Set<E> deleteUC = new HashSet<E>();
+
+
+
+ /**
+ * When objects are removed (either from Java or Graph), after remove processing the Java objects remain in mapping cache.
+ * This causes problems with Undo and Redo, whcih the end up re-using the removed objects from mapping cache.
+ *
+ * This code here synchronizes removed and added objects to collect deletable objects. (a deletable object is one which is removed but not added).
+ *
+ */
+ @SuppressWarnings("unused")
+ protected void syncDeletes() {
+ deleteUC.clear();
+ for (Pair<E, String> n : removed) {
+ deleteUC.add(n.first);
+ }
+ for (Pair<E, String> n : added) {
+ deleteUC.remove(n.first);
+ }
+ if (DEBUG && deleteUC.size() > 0) {
+ System.out.println("Delete sync");
+ for (E n : delete) {
+ System.out.println(debugString(n));
+ }
+ }
+ delete.addAll(deleteUC);
+ deleteUC.clear();
+ }
+
+ /**
+ * Clears deletable objects from mapping cache.
+ */
+ @SuppressWarnings("unused")
+ protected void clearDeletes() {
+ if (DEBUG && delete.size() > 0) System.out.println("Delete");
+ for (E n : delete) {
+ if (DEBUG) System.out.println(debugString(n));
+ mapping.getRange().remove(n);
+ }
+ delete.clear();
+ }
+
+ protected String debugString(E n) {
+ return n + "@" + Integer.toHexString(n.hashCode());
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void updateCycle() {
+ rem.clear();
+ add.clear();
+ mod.clear();
+ propagation.clear();
+
+
+ synchronized (syncMutex) {
+ // Check for overlapping additions and deletions, prevent deleting objects that are also added.
+ Deque<E> stack = new ArrayDeque<E>();
+ for (Pair<E, String> n : added) {
+ stack.add(n.first);
}
- }
-
- if (propagation.size() > 0) {
- stack.clear();
- stack.addAll(propagation);
- propagation.clear();
while (!stack.isEmpty()) {
- E node = stack.pop();
- if (propagation.contains(node))
- continue;
- propagation.add(node);
- for (NodeListener l : node.getListeners()) {
- if (l == this) {
- //changeTracking = false;
- //l.propertyChanged(node, G3D.URIs.hasPosition);
- //changeTracking = true;
- } else {
- l.propertyChanged(node, G3D.URIs.hasWorldPosition);
+ E n = stack.pop();
+ for (int i = removed.size()-1; i >= 0; i--) {
+ if (removed.get(i).first == n) {
+ removed.remove(i);
+ break;
}
}
- if (node instanceof ParentNode) {
- stack.addAll(((ParentNode<E>)node).getNodes());
+ if (n instanceof ParentNode) {
+ ParentNode<INode> pn = (ParentNode<INode>)n;
+ for (INode cn : pn.getNodes()) {
+ stack.push((E)cn);
+ }
}
}
- }
-
+
+ rem.addAll(removed);
+ add.addAll(added);
+ for (E e : updated.getKeys()) {
+ for (String s : updated.getValues(e)) {
+ mod.add(e, s);
+ }
+ }
+ syncDeletes();
+ removed.clear();
+ added.clear();
+ updated.clear();
+ }
+
+ for (Pair<E, String> n : rem) {
+ stopListening(n.first);
+ removeActor(n.first);
+ }
+
+ for (Pair<E, String> n : add) {
+ addActor(n.first);
+ listen(n.first);
+ }
+
+ for (E e : mod.getKeys()) {
+ Set<String> ids = mod.getValues(e);
+ if (ids.contains(G3D.URIs.hasPosition) || ids.contains(G3D.URIs.hasOrientation)) {
+ if (!propagation.contains(e))
+ propagation.add(e);
+ }
+ }
+
+ if (propagation.size() > 0) {
+ stack.clear();
+ stack.addAll(propagation);
+ propagation.clear();
+ while (!stack.isEmpty()) {
+ E node = stack.pop();
+ if (propagation.contains(node))
+ continue;
+ propagation.add(node);
+ for (NodeListener l : node.getListeners()) {
+ if (l == this) {
+ //changeTracking = false;
+ //l.propertyChanged(node, G3D.URIs.hasPosition);
+ //changeTracking = true;
+ } else {
+ l.propertyChanged(node, G3D.URIs.hasWorldPosition);
+ }
+ }
+ if (node instanceof ParentNode) {
+ stack.addAll(((ParentNode<E>)node).getNodes());
+ }
+ }
+ }
+
// synchronized (syncMutex) {
// rem.addAll(removed);
// add.addAll(added);
// added.clear();
// updated.clear();
// }
-
- for (E e : mod.getKeys()) {
- Set<String> ids = mod.getValues(e);
- updateActor(e,ids);
- }
-
-
- for (Pair<E, String> n : rem) {
- for (NodeListener l : nodeListeners)
- l.nodeRemoved(null, n.first, n.second);
- }
- for (Pair<E, String> n : add) {
- for (NodeListener l : nodeListeners)
- l.nodeAdded(n.first.getParent(), n.first, n.second);
- }
+
+ for (E e : mod.getKeys()) {
+ Set<String> ids = mod.getValues(e);
+ updateActor(e,ids);
+ }
+
+
+ for (Pair<E, String> n : rem) {
+ for (NodeListener l : nodeListeners)
+ l.nodeRemoved(null, n.first, n.second);
+ }
+ for (Pair<E, String> n : add) {
+ for (NodeListener l : nodeListeners)
+ l.nodeAdded(n.first.getParent(), n.first, n.second);
+ }
// for (Pair<E, String> n : mod) {
// for (NodeListener l : nodeListeners)
// l.propertyChanged(n.first, n.second);
// }
- for (E e : mod.getKeys()) {
- for (NodeListener l : nodeListeners)
- for (String s : mod.getValues(e))
- l.propertyChanged(e, s);
- }
-
- synchronized (syncMutex) {
- if (added.isEmpty() && removed.isEmpty() && updated.getKeys().size() == 0)
- rangeModified = false;
- }
- }
-
- @SuppressWarnings("unchecked")
- private void listen(INode node) {
- node.addListener(this);
- if (node instanceof ParentNode<?>) {
- ParentNode<INode> parentNode = (ParentNode<INode>)node;
- for (INode n : parentNode.getNodes())
- listen(n);
- }
- }
-
- private void stopListening(INode node) {
- node.removeListener(this);
- if (node instanceof ParentNode<?>) {
- @SuppressWarnings("unchecked")
- ParentNode<INode> parentNode = (ParentNode<INode>)node;
- for (INode n : parentNode.getNodes())
- stopListening(n);
- }
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public void propertyChanged(INode node, String id) {
- //receiveUpdate((E)node, id, graphUpdates);
- receiveUpdate((E)node, id, graphModified.contains(node));
-
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public <T extends INode> void nodeAdded(ParentNode<T> node, INode child,
- String rel) {
- if (DEBUG) System.out.println("Node added " + child + " parent " + node);
- //receiveAdd((E)child, rel ,graphUpdates);
- receiveAdd((E)child, rel ,graphModified.contains(node));
-
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public <T extends INode> void nodeRemoved(ParentNode<T> node, INode child,
- String rel) {
- if (DEBUG) System.out.println("Node removed " + child + " parent " + node);
- //receiveRemove((E)child, rel, graphUpdates);
- receiveRemove((E)child, rel, graphModified.contains(node));
-
- //FIXME : sometimes removed structural models cause ObjMap to add their children again.
- // removing the listener here prevents corruption of visual model, but better fix is needed.
- stopListening(child);
- }
-
- @Override
- public void delete() {
-
- changeTracking = false;
- mapping.removeMappingListener(this);
+ for (E e : mod.getKeys()) {
+ for (NodeListener l : nodeListeners)
+ for (String s : mod.getValues(e))
+ l.propertyChanged(e, s);
+ }
+
+ synchronized (syncMutex) {
+ if (added.isEmpty() && removed.isEmpty() && updated.getKeys().size() == 0)
+ rangeModified = false;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void listen(INode node) {
+ node.addListener(this);
+ if (node instanceof ParentNode<?>) {
+ ParentNode<INode> parentNode = (ParentNode<INode>)node;
+ for (INode n : parentNode.getNodes())
+ listen(n);
+ }
+ }
+
+ private void stopListening(INode node) {
+ node.removeListener(this);
+ if (node instanceof ParentNode<?>) {
+ @SuppressWarnings("unchecked")
+ ParentNode<INode> parentNode = (ParentNode<INode>)node;
+ for (INode n : parentNode.getNodes())
+ stopListening(n);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void propertyChanged(INode node, String id) {
+ //receiveUpdate((E)node, id, graphUpdates);
+ receiveUpdate((E)node, id, graphModified.contains(node));
+
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T extends INode> void nodeAdded(ParentNode<T> node, INode child,
+ String rel) {
+ if (DEBUG) System.out.println("Node added " + child + " parent " + node);
+ //receiveAdd((E)child, rel ,graphUpdates);
+ receiveAdd((E)child, rel ,graphModified.contains(node));
+
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T extends INode> void nodeRemoved(ParentNode<T> node, INode child,
+ String rel) {
+ if (DEBUG) System.out.println("Node removed " + child + " parent " + node);
+ //receiveRemove((E)child, rel, graphUpdates);
+ receiveRemove((E)child, rel, graphModified.contains(node));
+
+ //FIXME : sometimes removed structural models cause ObjMap to add their children again.
+ // removing the listener here prevents corruption of visual model, but better fix is needed.
+ stopListening(child);
+ }
+
+ @Override
+ public void delete() {
+
+ changeTracking = false;
+ mapping.removeMappingListener(this);
- for (E node : nodes) {
- node.removeListener(this);
- removeActor(node);
- node.cleanup();
- }
- nodes.clear();
- }
-
-
- private List<NodeListener> nodeListeners = new ArrayList<NodeListener>();
- @Override
- public void addListener(NodeListener listener) {
- nodeListeners.add(listener);
-
- }
-
- @Override
- public void removeListener(NodeListener listener) {
- nodeListeners.remove(listener);
-
- }
-
- public IMapping<Object,E> getMapping() {
- return mapping;
- }
-
-
+ for (E node : nodes) {
+ node.removeListener(this);
+ removeActor(node);
+ node.cleanup();
+ }
+ nodes.clear();
+ }
+
+
+ private List<NodeListener> nodeListeners = new ArrayList<NodeListener>();
+ @Override
+ public void addListener(NodeListener listener) {
+ nodeListeners.add(listener);
+
+ }
+
+ @Override
+ public void removeListener(NodeListener listener) {
+ nodeListeners.remove(listener);
+
+ }
+
+ @Override
+ public IMapping<DBObject,INode> getMapping() {
+ return mapping;
+ }
+
+
}