Merge "Documented difference of RuntimeEnvironmentRequest and Runti...quest2"
authorTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Tue, 9 Oct 2018 10:48:21 +0000 (10:48 +0000)
committerGerrit Code Review <gerrit2@simantics>
Tue, 9 Oct 2018 10:48:21 +0000 (10:48 +0000)
bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/ReadGraphImpl.java
bundles/org.simantics.document/src/org/simantics/document/function/WikiDocumentNodeImpl.java
bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/modelBrowser/handlers/StandardCopyHandler.java
bundles/org.simantics.scenegraph.ui/src/org/simantics/scenegraph/ui/NodeProxy.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/INode.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/Node.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/ParentNode.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/spatial/RTreeNode.java
bundles/org.simantics.views.swt.client/src/org/simantics/views/swt/client/base/SWTParentNode.java
bundles/org.simantics.views.swt.client/src/org/simantics/views/swt/client/base/SWTRoot.java
bundles/org.simantics.views.swt.client/src/org/simantics/views/swt/client/impl/SWTAlternative.java

index 77af46d557e740e8d73e4bc9c9252b2adb7e7a98..3feb22e00ebda688379b6ed7c8785b09fb8035ca 100644 (file)
@@ -18,7 +18,6 @@ import java.io.IOException;
 import java.io.PrintStream;
 import java.lang.reflect.Array;
 import java.lang.reflect.InvocationTargetException;
-import java.nio.BufferUnderflowException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -135,7 +134,6 @@ import org.simantics.db.exception.NoSingleResultException;
 import org.simantics.db.exception.ResourceNotFoundException;
 import org.simantics.db.exception.ServiceException;
 import org.simantics.db.exception.ValidationException;
-import org.simantics.db.impl.BlockingAsyncProcedure;
 import org.simantics.db.impl.RelationContextImpl;
 import org.simantics.db.impl.ResourceImpl;
 import org.simantics.db.impl.internal.RandomAccessValueSupport;
@@ -1107,9 +1105,10 @@ public class ReadGraphImpl implements AsyncReadGraph {
 
                assert (subject != null);
 
+               byte[] bytes = null;
                try {
                        
-                       byte[] bytes = processor.getValue(this, subject);
+                       bytes = processor.getValue(this, subject);
                        if (bytes == null) throw new DoesNotContainValueException("No value for resource " + subject);
 
                        Serializer serializer = getSerializer(binding);
@@ -1119,20 +1118,9 @@ public class ReadGraphImpl implements AsyncReadGraph {
 
                        throw new DoesNotContainValueException(e);
 
-               } catch (IOException e) {
-
-                       throw new ServiceException(e);
-
-               } catch (DatabaseException e) {
-                   
-            throw new ServiceException(e);
-            
-        } catch (BufferUnderflowException e) {
-            // This is sometimes thrown when deserialize fails because wrong format.
-            // For callers of this method this is just an service exception.
-            throw new ServiceException(e);
-        }
-
+               } catch (Throwable t) {
+                       throw new ServiceException("Could not getValue for subject " + debugString(subject) + " and binding " + String.valueOf(binding) + " with bytes " + safeArrayToString(bytes), t);
+               }
        }
 
        @Override
@@ -3544,14 +3532,9 @@ public class ReadGraphImpl implements AsyncReadGraph {
 //                                     else
                                                procedure.execute(graph, (T) obj);
 
-                               } catch (IOException e) {
-                                       procedure.exception(graph, e);
-                               } catch (BufferUnderflowException e) {
-                                       procedure.exception(graph, e);
                                } catch (Throwable t) {
-                                       procedure.exception(graph, t);
+                                   procedure.exception(graph, new ServiceException("Could not forValue for subject " + debugString(resource) + " and binding " + String.valueOf(binding) + " with bytes " + safeArrayToString(result), t));
                                }
-
                        }
 
                        @Override
@@ -3571,6 +3554,24 @@ public class ReadGraphImpl implements AsyncReadGraph {
                });
 
        }
+       
+    private static String safeArrayToString(byte[] a) {
+        if (a == null)
+            return "null";
+        int iMax = a.length - 1;
+        if (iMax == -1)
+            return "[]";
+
+        StringBuilder b = new StringBuilder();
+        b.append('[');
+        for (int i = 0; i < 100; i++) { // limit to first 100 items 
+            b.append(a[i]);
+            if (i == iMax)
+                return b.append(']').toString();
+            b.append(", ");
+        }
+        return b.append(", ... (" + a.length + ")]").toString();
+    }
 
        @Override
        public <T> void forValue(Resource subject, Binding binding,
@@ -4265,14 +4266,9 @@ public class ReadGraphImpl implements AsyncReadGraph {
                                        else
                                                procedure.execute(graph, (T) obj);
 
-                               } catch (IOException e) {
-                                       procedure.exception(graph, e);
-                               } catch (BufferUnderflowException e) {
-                                       procedure.exception(graph, e);
                                } catch (Throwable t) {
-                                       procedure.exception(graph, t);
+                                       procedure.exception(graph, new ServiceException("Could not forValue for subject " + debugString(resource) + " and binding " + String.valueOf(binding) + " with bytes " + safeArrayToString(result), t));
                                }
-
                        }
 
                        @Override
index 8987298289c882e7bec74f2c257678fe13dfe039..e3c52294c666630a3b706a2e333ba78ee79afaf0 100644 (file)
@@ -24,18 +24,18 @@ import org.simantics.scenegraph.ScenegraphUtils;
 import org.simantics.scl.runtime.function.Function1;
 import org.simantics.scl.runtime.function.Function2;
 
-abstract public class WikiDocumentNodeImpl extends ParentNode<WikiDocumentNode> implements WikiDocumentNode {
+public abstract class WikiDocumentNodeImpl extends ParentNode<WikiDocumentNode> implements WikiDocumentNode {
 
        public Boolean printInPDF = false;
        public String editText = null;
        
        private static final long serialVersionUID = 3394059912639648935L;
 
-       class M implements Map<String, WikiDocumentNode> {
-               
-               ArrayList<WikiDocumentNode> list = new ArrayList<WikiDocumentNode>();
-               Hashtable<String, WikiDocumentNode> table = new Hashtable<String, WikiDocumentNode>();
-               
+       static class M implements Map<String, INode> {
+
+               ArrayList<INode> list = new ArrayList<>();
+               Hashtable<String, INode> table = new Hashtable<>();
+
                @Override
                public void clear() {
                        table.clear();
@@ -50,11 +50,11 @@ abstract public class WikiDocumentNodeImpl extends ParentNode<WikiDocumentNode>
                        return table.containsValue(arg0);
                }
                @Override
-               public Set<java.util.Map.Entry<String, WikiDocumentNode>> entrySet() {
+               public Set<java.util.Map.Entry<String, INode>> entrySet() {
                        return table.entrySet();
                }
                @Override
-               public WikiDocumentNode get(Object arg0) {
+               public INode get(Object arg0) {
                        return table.get(arg0);
                }
                @Override
@@ -66,20 +66,20 @@ abstract public class WikiDocumentNodeImpl extends ParentNode<WikiDocumentNode>
                        return table.keySet();
                }
                @Override
-               public WikiDocumentNode put(String arg0, WikiDocumentNode arg1) {
-                       WikiDocumentNode exist = table.put(arg0, arg1);
+               public INode put(String arg0, INode arg1) {
+                       INode exist = table.put(arg0, arg1);
                        if(exist != null) list.remove(exist);
                        list.add(arg1);
                        return exist;
                }
                @Override
-               public void putAll(Map<? extends String, ? extends WikiDocumentNode> arg0) {
-                       for(Map.Entry<? extends String, ? extends WikiDocumentNode> entry : arg0.entrySet())
+               public void putAll(Map<? extends String, ? extends INode> arg0) {
+                       for(Map.Entry<? extends String, ? extends INode> entry : arg0.entrySet())
                                put(entry.getKey(), entry.getValue());
                }
                @Override
-               public WikiDocumentNode remove(Object arg0) {
-                       WikiDocumentNode node = table.remove(arg0);
+               public INode remove(Object arg0) {
+                       INode node = table.remove(arg0);
                        if(node != null) list.remove(node);
                        return node;
                }
@@ -88,22 +88,22 @@ abstract public class WikiDocumentNodeImpl extends ParentNode<WikiDocumentNode>
                        return table.size();
                }
                @Override
-               public Collection<WikiDocumentNode> values() {
+               public Collection<INode> values() {
                        return list;
                }
-               
+
        }
-       
+
        @Override
        final public void asyncRemoveNode(INode node) {
         throw new Error();
        }
        
        @Override
-       protected Map<String, WikiDocumentNode> createChildMap() {
+       protected Map<String, INode> createChildMap(int initialCapacity) {
                return new M();
        }
-       
+
        @Override
        public Function1<Object, Boolean> getPropertyFunction(String propertyName) {
                return ScenegraphUtils.getMethodPropertyFunction(null, this, propertyName);
@@ -119,7 +119,7 @@ abstract public class WikiDocumentNodeImpl extends ParentNode<WikiDocumentNode>
        }
 
        protected void createChildren(StringBuilder b, boolean isPDF) {
-               for(WikiDocumentNode node : children.values()) node.create(b, isPDF);
+               for(WikiDocumentNode node : getNodes()) node.create(b, isPDF);
        }
        
        public void synchronizePrintInPDF(Boolean value) {
index 002a31381439d3df4320a22dd79b28d19c012674..1ce5b2d3698ddee5ce7989059dc58de5d6a9fe04 100644 (file)
@@ -21,6 +21,7 @@ import org.eclipse.core.commands.ExecutionEvent;
 import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.SubMonitor;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.jface.action.IStatusLineManager;
@@ -97,12 +98,11 @@ public class StandardCopyHandler extends AbstractHandler {
         formatSelectionToClipboardText(event);
         final Resource[] rs = ResourceAdaptionUtils.toResources(selection);
         Job job = new Job("Copy resources") {
-            
             @Override
             protected IStatus run(IProgressMonitor monitor) {
                 monitor.beginTask("Copy resources to clipboard", 1);
                 copyResourcesToClipboard(rs, selection, SubMonitor.convert(monitor, 1));
-                return null;
+                return Status.OK_STATUS;
             }
         };
         job.setUser(true);
index 6ed294b6236b88d276247860798af52ff807821f..a9307c6e2f9868a9316dbc3d2b3ed8fcf1de8eac 100644 (file)
@@ -62,7 +62,7 @@ class NodeProxy {
         INode n = getNode();
         if (n == null)
             return "-";
-        return n.getId().toString();
+        return Long.toString(n.getId());
     }
 
     public String getId() {
index c14edca841903af5dabc69496689d845f1f98d78..755bb755f31a0ffd77628198378ca0db38a09430 100644 (file)
@@ -78,7 +78,7 @@ public interface INode extends Serializable {
      * 
      * @return unique node identifier
      */
-    public Long getId();
+    public long getId();
 
     /**
      * @return root node of the scene graph or <code>null</code> if this node is
@@ -109,8 +109,7 @@ public interface INode extends Serializable {
      * @see ParentNode#getOrCreateNode(String, Class)
      */
     public void init();
-    public void attach();
-    
+
     /**
      * Perform cleanup for this node and for the child nodes. Any resources
      * (including child nodes) related to this node are unusable after this
index 7ce4611a38e0afd2a807e4d409ba324df6e34285..b9fb1b67fc00ad9d79e9e29221745f64e265e860 100644 (file)
@@ -22,7 +22,13 @@ public abstract class Node implements INode {
 
     private static final long                  serialVersionUID       = -5540999051056414851L;
 
+    /**
+     * The scene graph is essentially single-threaded, meaning nodes should be
+     * created in one thread only. For this reason, we are not using AtomicLong
+     * here.
+     */
     public transient static long               IDCOUNTER              = 1;
+    protected long                             id                     = IDCOUNTER++;
 
     protected transient ParentNode<?>          parent                 = null;
     protected transient Location               location               = Location.LOCAL;
@@ -31,12 +37,10 @@ public abstract class Node implements INode {
     // Support for only one listener should be enough, thus we don't need PropertyChangeSupport
     protected transient PropertyChangeListener propertyChangeListener = null;
 
-    protected Long                             id                     = IDCOUNTER++;
-
     /**
      * @see org.simantics.scenegraph.INode#getId()
      */
-    public Long getId() {
+    public long getId() {
         return id;
     }
 
@@ -78,10 +82,6 @@ public abstract class Node implements INode {
      */
     public void init() {
     }
-    
-    public void attach() {
-    }
-    
 
     /**
      * @see org.simantics.scenegraph.INode#cleanup()
index 866e8d36cba6718fe20b5eebb926fe9698228e2a..e9e0b4be3e1f5beb2107b89733a1ac72ba04bc2d 100644 (file)
  *******************************************************************************/
 package org.simantics.scenegraph;
 
-import gnu.trove.map.hash.THashMap;
-
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.util.Collection;
-import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 
+import gnu.trove.map.hash.TLongObjectHashMap;
+
 /**
  * Base class of all scene graph nodes which can have a set of sub-nodes
  * (children). This class only provides support for unordered children.
@@ -33,11 +33,6 @@ public abstract class ParentNode<T extends INode> extends Node {
     public static final String                 UNLINK               = "#UNLINK#";
     public static final String                 NULL                 = "#NULL#";
 
-    protected static final String[]            EMPTY_STRING_ARRAY   = {};
-
-    @SuppressWarnings("rawtypes")
-    private static final Map                   DISPOSED_CHILDREN    = Collections.emptyMap();
-
     /**
      * A value used for {@link #rootNodeCache} to indicate the node has been
      * disposed and the root node cache is to be considered indefinitely
@@ -51,7 +46,8 @@ public abstract class ParentNode<T extends INode> extends Node {
         }
     };
 
-    protected transient Map<String, T>         children             = createChildMap();
+    protected transient Map<String, INode>       children      = createChildMap();
+    private transient TLongObjectHashMap<String> childrenIdMap = new TLongObjectHashMap<>();
 
     /**
      * A cached value for the root node of this parent node. This makes it
@@ -64,50 +60,57 @@ public abstract class ParentNode<T extends INode> extends Node {
      */
     protected transient volatile ParentNode<?> rootNodeCache;
 
-    protected Map<String, T> createChildMap() {
-       return new THashMap<String, T>(1);
+    protected Map<String, INode> createChildMap() {
+        return createChildMap(1);
     }
-    
+
+    protected Map<String, INode> createChildMap(int initialCapacity) {
+        // With JDK 1.8 HashMap is faster than Trove
+        return new HashMap<>(initialCapacity);
+    }
+
     public final <TC> TC addNode(Class<TC> a) {
         return addNode(java.util.UUID.randomUUID().toString(), a);
     }
 
-    @SuppressWarnings("unchecked")
     public <TC extends INode> TC addNode(String id, TC child) {
+        return addNodeInternal(id, child, true, true);
+    }
 
+    private <TC extends INode> TC addNodeInternal(String id, TC child, boolean init, boolean addToChildren) {
         child.setParent(this);
-        children.put(id, (T)child);
+        if (addToChildren)
+            children.put(id, child);
+
+        childrenIdMap.put(child.getId(), id);
 
-        child.init();
+        if (init)
+            child.init();
         childrenChanged();
         return (TC)child;
-       
     }
 
-    @SuppressWarnings("unchecked")
     public <TC extends INode> TC attachNode(String id, TC child) {
-
-        child.setParent(this);
-        children.put(id, (T)child);
-
-        child.attach();
-        childrenChanged();
-        return (TC)child;
-       
+        return addNodeInternal(id, child, false, true);
     }
 
     @SuppressWarnings("unchecked")
     public <TC extends INode> TC detachNode(String id) {
-        T child = children.remove(id);
+        INode child = children.remove(id);
         if (child == null)
             return null;
+        childrenIdMap.remove(child.getId());
         child.setParent(null);
         childrenChanged();
         return (TC) child;
     }
 
-    @SuppressWarnings("unchecked")
     public <TC> TC addNode(String id, Class<TC> a) {
+        return addNodeInternal0(id, a, true);
+    }
+
+    @SuppressWarnings("unchecked")
+    private <TC> TC addNodeInternal0(String id, Class<TC> a, boolean addToChildren) {
         // a must be extended from Node
         if(!Node.class.isAssignableFrom(a)) {
             throw new IllegalArgumentException(a + " is not extended from org.simantics.scenegraph.Node");
@@ -120,75 +123,32 @@ public abstract class ParentNode<T extends INode> extends Node {
         } catch (IllegalAccessException e) {
             throw new NodeException("Node " + Node.getSimpleClassName(a) + " instantiation failed, see exception for details.", e);
         }
-
-        child.setParent(this);
-        children.put(id, (T)child);
-
-        child.init();
-        childrenChanged();
-        return (TC)child;
+        return (TC) addNodeInternal(id, child, true, addToChildren);
     }
 
     @SuppressWarnings("unchecked")
     public final <TC extends INode> TC getOrAttachNode(String id, TC a) {
-        synchronized (children) {
-            if (children.containsKey(id))
-                return (TC) children.get(id);
-        }
-        return attachNode(id, a);
+        return (TC) children.computeIfAbsent(id, key -> {
+            return (T) addNodeInternal(id, a, false, false);
+        });
     }
     
     @SuppressWarnings("unchecked")
     public final <TC> TC getOrCreateNode(String id, Class<TC> a) {
-        synchronized (children) {
-            if (children.containsKey(id))
-                return (TC) children.get(id);
-        }
-        return addNode(id, a);
+        return (TC) children.computeIfAbsent(id, key -> {
+            return (T) addNodeInternal0(id, a, false);
+        });
     }
 
     public final void removeNode(String id) {
-        INode child = null;
-        synchronized (children) {
-            child = children.remove(id);
-        }
-        if (child != null) {
-            if (child instanceof ParentNode<?>) {
-                ((ParentNode<?>) child).removeNodes();
-            }
-            child.cleanup();
-            child.setParent(null);
-            childrenChanged();
-            if (propertyChangeListener != null) {
-                propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", child.getClass(), NULL)); // "children" is a special field name
-            }
-        }
+        INode child = children.remove(id);
+        if (child != null)
+            removeNodeInternal(child, true);
     }
 
     public final void removeNode(INode child) {
-        synchronized (children) {
-            String id = null;
-            // FIXME: damn slow, needs more data structure to be supported well
-            // One option would be to store the id<->node mappings in a BidiMap.
-            for (String tmp : children.keySet()) {
-                if (children.get(tmp).equals(child)) {
-                    id = tmp;
-                    break;
-                }
-            }
-            if(id == null) return;
-            children.remove(id);
-            childrenChanged();
-        }
-        if (child instanceof ParentNode<?>) {
-            ((ParentNode<?>) child).removeNodes();
-        }
-        child.cleanup();
-        child.setParent(null);
-
-        if (propertyChangeListener != null) {
-            propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", child.getClass(), NULL)); // "children" is a special field name
-        }
+        String key = childrenIdMap.get(child.getId());
+        removeNode(key);
     }
 
     /**
@@ -196,25 +156,25 @@ public abstract class ParentNode<T extends INode> extends Node {
      * children).
      */
     public final void removeNodes() {
-        synchronized (children) {
-            boolean changed = false;
-            String[] keys = children.keySet().toArray(EMPTY_STRING_ARRAY);
-            for (String key : keys) {
-                INode child = children.remove(key);
-                if (child != null) {
-                    changed = true;
-                    if (child instanceof ParentNode<?>) {
-                        ((ParentNode<?>) child).removeNodes();
-                    }
-                    child.cleanup();
-                    child.setParent(null);
-                    if (propertyChangeListener != null) {
-                        propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", child.getClass(), NULL)); // "children" is a special field name
-                    }
-                }
+        boolean changed = children.size() > 0;
+        children.forEach((id, child) -> removeNodeInternal(child, false));
+        children.clear();
+        childrenIdMap.clear();
+
+        if (changed)
+            childrenChanged();
+    }
+
+    private void removeNodeInternal(INode child, boolean triggerChildrenChanged) {
+        if (child != null) {
+            if (child instanceof ParentNode<?>) {
+                ((ParentNode<?>) child).removeNodes();
             }
-            if (changed)
+            child.cleanup();
+            child.setParent(null);
+            if (triggerChildrenChanged)
                 childrenChanged();
+            triggerPropertyChangeEvent(child);
         }
     }
 
@@ -228,8 +188,9 @@ public abstract class ParentNode<T extends INode> extends Node {
 
     public abstract void asyncRemoveNode(INode node);
 
+    @SuppressWarnings("unchecked")
     public T getNode(String id) {
-        return children.get(id);
+        return (T) children.get(id);
     }
 
     /**
@@ -243,8 +204,9 @@ public abstract class ParentNode<T extends INode> extends Node {
     /**
      * @return the collection of this node's children in an unspecified order
      */
+    @SuppressWarnings("unchecked")
     public Collection<T> getNodes() {
-        return children.values();
+        return (Collection<T>) children.values();
     }
 
     public int getNodeCount() {
@@ -258,32 +220,28 @@ public abstract class ParentNode<T extends INode> extends Node {
      */
     public void setPropertyChangeListener(PropertyChangeListener propertyChangeListener) {
         this.propertyChangeListener = propertyChangeListener;
-        synchronized(children) {
-            for(T t : children.values()) {
-                INode child = t;
-                if(child instanceof ParentNode<?>) {
-                    ((ParentNode<?>)child).setPropertyChangeListener(propertyChangeListener);
-                } else {
-                    ((Node)child).propertyChangeListener = propertyChangeListener; // FIXME
-                }
+        children.forEach((id, child) -> {
+            if(child instanceof ParentNode<?>) {
+                ((ParentNode<?>)child).setPropertyChangeListener(propertyChangeListener);
+            } else {
+                ((Node)child).propertyChangeListener = propertyChangeListener; // FIXME
             }
-        }
+        });
     }
 
-    @SuppressWarnings("unchecked")
     @Override
     public void cleanup() {
         retractMapping();
-        if (children != DISPOSED_CHILDREN) { 
-            synchronized(children) {
-                for(T child : children.values()) {
-                    ((INode)child).cleanup();
-                    ((INode)child).setParent(null);
-                }
-                children.clear();
-                children = DISPOSED_CHILDREN;
-                childrenChanged();
-            }
+        if (children != null) { 
+            children.forEach((id, child) -> {
+                child.cleanup();
+                child.setParent(null);
+            });
+            children.clear();
+            childrenIdMap.clear();
+            children = null;
+            childrenIdMap = null;
+            childrenChanged();
             rootNodeCache = DISPOSED;
         }
     }
@@ -294,13 +252,12 @@ public abstract class ParentNode<T extends INode> extends Node {
             return;
         }
 
-        synchronized (children) {
-            // 1. Add children under parent
-            parent.appendChildren(children);
+        // 1. Add children under parent
+        parent.appendChildren(children);
 
-            // 2. Clear children
-            children.clear();
-        }
+        // 2. Clear child maps to prevent cleanup from deleting them in step 4. 
+        children.clear();
+        childrenIdMap.clear();
 
         // 3. Remove this node from parent
         parent.unlinkChild(this);
@@ -313,30 +270,21 @@ public abstract class ParentNode<T extends INode> extends Node {
      * Helper method for delete()
      * @param children
      */
-    @SuppressWarnings("unchecked")
-    protected void appendChildren(Map<String, ?> children) {
-        synchronized(this.children) {
-            for(String id : children.keySet()) {
-                INode child = (INode)children.get(id);
-                this.children.put(id, (T)child); // Hopefully cast works
-                child.setParent(this);
-
-                // Send notify only if we are on server side (or standalone)
-                if (propertyChangeListener != null && location.equals(Location.LOCAL)) {
-                    propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", null, EXISTING)); // "children" is a special field name
-                }
-            }
-        }
+    protected void appendChildren(Map<String, INode> children) {
+        children.forEach((key, value) -> {
+            appendChildInternal(key, value);
+        });
     }
 
-    @SuppressWarnings("unchecked")
     protected void appendChild(String id, INode child) {
-        children.put(id, (T)child);
+        appendChildInternal(id, child);
+    }
+    
+    private void appendChildInternal(String id, INode child) {
+        children.put(id, child);
+        childrenIdMap.put(child.getId(), id);
         child.setParent(this);
-        // Send notify only if we are on server side (or standalone)
-        if (propertyChangeListener != null && location.equals(Location.LOCAL)) {
-            propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+(child).getId()+"]", null, EXISTING)); // "children" is a special field name
-        }
+        triggerPropertyChangeEvent(child);
     }
 
     /**
@@ -344,22 +292,17 @@ public abstract class ParentNode<T extends INode> extends Node {
      * @param child
      */
     protected void unlinkChild(INode child) {
-        synchronized(children) {
-            String id = null;
-            // FIXME: damn slow, needs more data structure to be supported well
-            // One option would be to store the id<->node mappings in a BidiMap.
-            for(String tmp : children.keySet()) {
-                if(children.get(tmp).equals(child)) {
-                    id = tmp;
-                    break;
-                }
-            }
-            if(id == null) return;
+        String id = childrenIdMap.remove(child.getId());
+        if(id != null) {
             children.remove(id);
             childrenChanged();
+            triggerPropertyChangeEvent(child);
         }
+    }
 
-        if(propertyChangeListener != null && location.equals(Location.LOCAL)) {
+    private void triggerPropertyChangeEvent(INode child) {
+        // Send notify only if we are on server side (or standalone)
+        if (propertyChangeListener != null && location.equals(Location.LOCAL)) {
             propertyChangeListener.propertyChange(new PropertyChangeEvent(this, "children["+child.getId()+"]", child.getClass(), UNLINK)); // "children" is a special field name
         }
     }
index 52403edd02ea251522270f43ec9904614cda8c0b..56b6be302e44910e22fb62f47310406ba2c419e8 100644 (file)
@@ -20,9 +20,11 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 
+import org.simantics.scenegraph.INode;
 import org.simantics.scenegraph.g2d.G2DParentNode;
 import org.simantics.scenegraph.g2d.IG2DNode;
 import org.simantics.scenegraph.g2d.events.Event;
@@ -114,6 +116,11 @@ public class RTreeNode extends G2DParentNode implements INodeEventHandlerProvide
     private transient ArrayList<IG2DNode> collected  = new ArrayList<IG2DNode>();
     private transient Set<IG2DNode>       simplified = new HashSet<IG2DNode>();
 
+    @Override
+    protected Map<String, INode> createChildMap() {
+        return super.createChildMap(1 << 15);
+    }
+
     @Override
     public void render(Graphics2D g) {
         if (DISABLE_RTREE) {
index 1d0a254423088b4f2793f24329738f04e1cf854d..3fc213126d50b03b9cd5f14e27dfe65b13e60939 100644 (file)
@@ -13,17 +13,17 @@ import org.eclipse.ui.IWorkbenchSite;
 import org.simantics.scenegraph.INode;
 import org.simantics.scenegraph.ParentNode;
 
-abstract public class SWTParentNode extends ParentNode<ISWTViewNode> implements ISWTViewNode {
+public abstract class SWTParentNode extends ParentNode<ISWTViewNode> implements ISWTViewNode {
 
        private boolean disposed = false;
-       
+
        private static final long serialVersionUID = -3548136282051185971L;
 
-       class M implements Map<String, ISWTViewNode> {
-               
-               ArrayList<ISWTViewNode> list = new ArrayList<ISWTViewNode>();
-               Hashtable<String, ISWTViewNode> table = new Hashtable<String, ISWTViewNode>();
-               
+       static class M implements Map<String, INode> {
+
+               ArrayList<INode> list = new ArrayList<>();
+               Hashtable<String, INode> table = new Hashtable<>();
+
                @Override
                public void clear() {
                        table.clear();
@@ -38,11 +38,11 @@ abstract public class SWTParentNode extends ParentNode<ISWTViewNode> implements
                        return table.containsValue(arg0);
                }
                @Override
-               public Set<java.util.Map.Entry<String, ISWTViewNode>> entrySet() {
+               public Set<java.util.Map.Entry<String, INode>> entrySet() {
                        return table.entrySet();
                }
                @Override
-               public ISWTViewNode get(Object arg0) {
+               public INode get(Object arg0) {
                        return table.get(arg0);
                }
                @Override
@@ -54,20 +54,20 @@ abstract public class SWTParentNode extends ParentNode<ISWTViewNode> implements
                        return table.keySet();
                }
                @Override
-               public ISWTViewNode put(String arg0, ISWTViewNode arg1) {
-                       ISWTViewNode exist = table.put(arg0, arg1);
+               public INode put(String arg0, INode arg1) {
+                       INode exist = table.put(arg0, arg1);
                        if(exist != null) list.remove(exist);
                        list.add(arg1);
                        return exist;
                }
                @Override
-               public void putAll(Map<? extends String, ? extends ISWTViewNode> arg0) {
-                       for(Map.Entry<? extends String, ? extends ISWTViewNode> entry : arg0.entrySet())
+               public void putAll(Map<? extends String, ? extends INode> arg0) {
+                       for(Map.Entry<? extends String, ? extends INode> entry : arg0.entrySet())
                                put(entry.getKey(), entry.getValue());
                }
                @Override
-               public ISWTViewNode remove(Object arg0) {
-                       ISWTViewNode node = table.remove(arg0);
+               public INode remove(Object arg0) {
+                       INode node = table.remove(arg0);
                        if(node != null) list.remove(node);
                        return node;
                }
@@ -76,12 +76,12 @@ abstract public class SWTParentNode extends ParentNode<ISWTViewNode> implements
                        return table.size();
                }
                @Override
-               public Collection<ISWTViewNode> values() {
+               public Collection<INode> values() {
                        return list;
                }
-               
+
        }
-       
+
        @Override
        public SWTRoot getRootNode() {
                ParentNode<?> root = super.getRootNode();
@@ -104,18 +104,18 @@ abstract public class SWTParentNode extends ParentNode<ISWTViewNode> implements
        final public void asyncRemoveNode(INode node) {
                throw new Error();
        }
-       
+
        @Override
-       protected Map<String, ISWTViewNode> createChildMap() {
+       protected Map<String, INode> createChildMap(int initialCapacity) {
                return new M();
        }
-       
+
        protected void createChildComposites() {
                createChildComposites((Composite)getControl());
        }
 
        protected void createChildComposites(Composite composite) {
-               for(ISWTViewNode node : children.values()) node.createControls(composite);
+               for(ISWTViewNode node : getNodes()) node.createControls(composite);
        }
        
        @Override
@@ -133,7 +133,7 @@ abstract public class SWTParentNode extends ParentNode<ISWTViewNode> implements
        }
 
        public Collection<ISWTViewNode> getChildComposites() {
-               return children.values();
+               return getNodes();
        }
        
        @Override
index 36f874d787a4d5486bd14f45977535169ad19e1b..59c0dde6eb009e004fcf5a137bae55da3aeaca6a 100644 (file)
@@ -62,12 +62,12 @@ public class SWTRoot extends SWTParentNode implements ISWTViewNode {
        public void createControls(Composite parent) {
                body = parent;
                resourceManager = new LocalResourceManager(JFaceResources.getResources(), body);
-               for(ISWTViewNode child : children.values()) child.createControls(body);
+               for(ISWTViewNode child : getNodes()) child.createControls(body);
        }
        
        public Control getSingleChild() {
                if(children.size() != 1) throw new IllegalStateException("SWTRoot should have exactly one child (has " + children.size() + ").");
-               return children.values().iterator().next().getControl();
+               return getNodes().iterator().next().getControl();
        }
        
 }
index cd4720636d3d4ac6bd7a4c6d0b268e0371cece8c..f311f4369900142c889e8abb1b6cbbe30cf60bff 100644 (file)
@@ -28,7 +28,7 @@ public class SWTAlternative extends SingleSWTViewNode<Composite> {
        GridLayoutFactory.fillDefaults().applyTo(control);
        GridDataFactory.fillDefaults().grab(true, true).applyTo(control);
        
-       Collection<ISWTViewNode> nodes = children.values();
+       Collection<ISWTViewNode> nodes = getNodes();
        if(nodes.size() > 0) {
                Iterator<ISWTViewNode> it = nodes.iterator();
                if(condition == null || !condition) {