Fixed NPE problems from ParentNode by reintroducing child map sentinels 07/2307/1
authorTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Thu, 11 Oct 2018 13:15:15 +0000 (16:15 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Thu, 11 Oct 2018 13:15:15 +0000 (16:15 +0300)
gitlab #65

Change-Id: I63778c368f197a4b92e71c96a37fd569ad7cb51c

bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/ParentNode.java

index e9e0b4be3e1f5beb2107b89733a1ac72ba04bc2d..976857afe1fbc85349a1bcb3e5869782ff5f9b48 100644 (file)
@@ -14,9 +14,11 @@ package org.simantics.scenegraph;
 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.TLongObjectMap;
 import gnu.trove.map.hash.TLongObjectHashMap;
 
 /**
@@ -46,8 +48,41 @@ public abstract class ParentNode<T extends INode> extends Node {
         }
     };
 
-    protected transient Map<String, INode>       children      = createChildMap();
-    private transient TLongObjectHashMap<String> childrenIdMap = new TLongObjectHashMap<>();
+    private static class ImmutableIdMap extends TLongObjectHashMap<String> {
+        private static final String MSG = "immutable singleton instance";
+
+        @Override
+        public String put(long key, String value) {
+            throw new UnsupportedOperationException(MSG);
+        }
+        @Override
+        public void putAll(Map<? extends Long, ? extends String> map) {
+            throw new UnsupportedOperationException(MSG);
+        }
+        @Override
+        public void putAll(TLongObjectMap<? extends String> map) {
+            throw new UnsupportedOperationException(MSG);
+        }
+        @Override
+        public String putIfAbsent(long key, String value) {
+            throw new UnsupportedOperationException(MSG);
+        }
+    }
+
+    /**
+     * This is the value given to {@link #children} when this node is disposed and
+     * cleaned up.
+     */
+    private static final Map<String, INode> DISPOSED_CHILDREN = Collections.emptyMap();
+
+    /**
+     * This is the value given to {@link #childrenIdMap} when this node is disposed
+     * and cleaned up.
+     */
+    private static final TLongObjectMap<String> DISPOSED_CHILDREN_ID_MAP = new ImmutableIdMap();
+
+    protected transient Map<String, INode>   children  = createChildMap();
+    private transient TLongObjectMap<String> childrenIdMap = new TLongObjectHashMap<>();
 
     /**
      * A cached value for the root node of this parent node. This makes it
@@ -232,15 +267,15 @@ public abstract class ParentNode<T extends INode> extends Node {
     @Override
     public void cleanup() {
         retractMapping();
-        if (children != null) { 
+        if (children != DISPOSED_CHILDREN) { 
             children.forEach((id, child) -> {
                 child.cleanup();
                 child.setParent(null);
             });
             children.clear();
             childrenIdMap.clear();
-            children = null;
-            childrenIdMap = null;
+            children = DISPOSED_CHILDREN;
+            childrenIdMap = DISPOSED_CHILDREN_ID_MAP;
             childrenChanged();
             rootNodeCache = DISPOSED;
         }