X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=org.simantics.g3d%2Fsrc%2Forg%2Fsimantics%2Fg3d%2Fscl%2FScriptNodeMap.java;h=b7a7de03e8b52f244e42aa49f45b73e308f8c389;hb=d207cfa3235339e7d7b894494be400160b121bd5;hp=4af8e5a0b829a8e3caf2154c3f490db49da7e8ad;hpb=11cf079ab699e65f6c97b86745a3c8cbd65817b8;p=simantics%2F3d.git diff --git a/org.simantics.g3d/src/org/simantics/g3d/scl/ScriptNodeMap.java b/org.simantics.g3d/src/org/simantics/g3d/scl/ScriptNodeMap.java index 4af8e5a0..b7a7de03 100644 --- a/org.simantics.g3d/src/org/simantics/g3d/scl/ScriptNodeMap.java +++ b/org.simantics.g3d/src/org/simantics/g3d/scl/ScriptNodeMap.java @@ -1,15 +1,17 @@ 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; import java.util.Stack; import org.simantics.db.ReadGraph; -import org.simantics.db.Session; +import org.simantics.db.RequestProcessor; import org.simantics.db.WriteGraph; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.common.request.WriteRequest; @@ -40,192 +42,186 @@ import org.simantics.utils.datastructures.Pair; * * @param */ -public abstract class ScriptNodeMap implements NodeMap, IMappingListener, NodeListener { +public abstract class ScriptNodeMap implements NodeMap, IMappingListener, NodeListener { - private static final boolean DEBUG = false; - - protected Session session; - protected IMapping mapping; - - protected ParentNode rootNode; - - protected Set nodes = new HashSet(); - - private boolean dirty = false; - - public ScriptNodeMap(Session session, IMapping mapping, ParentNode 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 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 getRenderObjects(INode node) { - return Collections.EMPTY_LIST; - } - - @SuppressWarnings("unchecked") - @Override - public ParentNode getRootNode() { - return (ParentNode)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 RequestProcessor session; + protected IMapping mapping; + + protected ParentNode rootNode; + + protected Set nodes = new HashSet(); + + private boolean dirty = false; + + public ScriptNodeMap(RequestProcessor session, IMapping mapping, ParentNode 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 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 getRenderObjects(INode node) { + return Collections.EMPTY_LIST; + } + + @Override + public ParentNode 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> added = new ArrayList>(); - private List> removed = new ArrayList>(); - private MapSet updated = new MapSet.Hash(); + private List> added = new ArrayList>(); + private List> removed = new ArrayList>(); + private MapSet updated = new MapSet.Hash(); - 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 n : added) { - if (n.first.equals(node)) - return; - } - if (changeTracking) { - mapping.rangeModified((E)node.getParent()); - } - added.add(new Pair(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 n : removed) { - if (n.first.equals(node)) - return; - } - if (changeTracking && !db) - mapping.rangeModified((E)node.getParent()); - removed.add(new Pair(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 n : added) { + if (n.first.equals(node)) + return; + } + if (changeTracking) { + mapping.rangeModified((E)node.getParent()); + } + added.add(new Pair(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 n : removed) { + if (n.first.equals(node)) + return; + } + if (changeTracking && !db) + mapping.rangeModified((E)node.getParent()); + removed.add(new Pair(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 n : updated) { // if (n.first.equals(node)) // return; // } - if (changeTracking && !db) - mapping.rangeModified(node); - //updated.add(new Pair(node, id)); - updated.add(node, id); - rangeModified = true; - } - repaint(); - } - - private boolean graphUpdates = false; - private Set graphModified = new HashSet(); - - 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; + if (changeTracking && !db) + mapping.rangeModified(node); + //updated.add(new Pair(node, id)); + updated.add(node, id); + rangeModified = true; + } + repaint(); + } + + private boolean graphUpdates = false; + private Set graphModified = new HashSet(); + + 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") @@ -234,191 +230,212 @@ public abstract class ScriptNodeMap implements NodeMap> rem = new ArrayList>(); // Removed objects - List> add = new ArrayList>(); // Added objects - MapSet mod = new MapSet.Hash(); // Modified objects - Set propagation = new HashSet(); // Objects with propagated changes - Stack stack = new Stack(); // Stack for handling propagation - Set delete = Collections.synchronizedSet(new HashSet()); // Objects to be completely deleted - Set deleteUC = new HashSet(); - - - - - /** - * 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 n : removed) { - deleteUC.add(n.first); - } - for (Pair 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 n : rem) { - stopListening(n.first); - removeActor(n.first); - } - - for (Pair n : add) { - addActor(n.first); - listen(n.first); - } - - for (E e : mod.getKeys()) { - Set 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> rem = new ArrayList>(); // Removed objects + List> add = new ArrayList>(); // Added objects + MapSet mod = new MapSet.Hash(); // Modified objects + Set propagation = new HashSet(); // Objects with propagated changes + Stack stack = new Stack(); // Stack for handling propagation + Set delete = Collections.synchronizedSet(new HashSet()); // Objects to be completely deleted + Set deleteUC = new HashSet(); + + + + /** + * 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 n : removed) { + deleteUC.add(n.first); + } + for (Pair 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 stack = new ArrayDeque(); + for (Pair 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)node).getNodes()); + if (n instanceof ParentNode) { + ParentNode pn = (ParentNode)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 n : rem) { + stopListening(n.first); + removeActor(n.first); + } + + for (Pair n : add) { + addActor(n.first); + listen(n.first); + } + + for (E e : mod.getKeys()) { + Set 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)node).getNodes()); + } + } + } + // synchronized (syncMutex) { // rem.addAll(removed); // add.addAll(added); @@ -432,119 +449,120 @@ public abstract class ScriptNodeMap implements NodeMap ids = mod.getValues(e); - updateActor(e,ids); - } - - - for (Pair n : rem) { - for (NodeListener l : nodeListeners) - l.nodeRemoved(null, n.first, n.second); - } - for (Pair n : add) { - for (NodeListener l : nodeListeners) - l.nodeAdded(n.first.getParent(), n.first, n.second); - } + + for (E e : mod.getKeys()) { + Set ids = mod.getValues(e); + updateActor(e,ids); + } + + + for (Pair n : rem) { + for (NodeListener l : nodeListeners) + l.nodeRemoved(null, n.first, n.second); + } + for (Pair n : add) { + for (NodeListener l : nodeListeners) + l.nodeAdded(n.first.getParent(), n.first, n.second); + } // for (Pair 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 parentNode = (ParentNode)node; - for (INode n : parentNode.getNodes()) - listen(n); - } - } - - private void stopListening(INode node) { - node.removeListener(this); - if (node instanceof ParentNode) { - @SuppressWarnings("unchecked") - ParentNode parentNode = (ParentNode)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 void nodeAdded(ParentNode 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 void nodeRemoved(ParentNode 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 parentNode = (ParentNode)node; + for (INode n : parentNode.getNodes()) + listen(n); + } + } + + private void stopListening(INode node) { + node.removeListener(this); + if (node instanceof ParentNode) { + @SuppressWarnings("unchecked") + ParentNode parentNode = (ParentNode)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 void nodeAdded(ParentNode 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 void nodeRemoved(ParentNode 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 nodeListeners = new ArrayList(); - @Override - public void addListener(NodeListener listener) { - nodeListeners.add(listener); - - } - - @Override - public void removeListener(NodeListener listener) { - nodeListeners.remove(listener); - - } - - public IMapping getMapping() { - return mapping; - } - - + for (E node : nodes) { + node.removeListener(this); + removeActor(node); + node.cleanup(); + } + nodes.clear(); + } + + + private List nodeListeners = new ArrayList(); + @Override + public void addListener(NodeListener listener) { + nodeListeners.add(listener); + + } + + @Override + public void removeListener(NodeListener listener) { + nodeListeners.remove(listener); + + } + + @Override + public IMapping getMapping() { + return mapping; + } + + }