]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.browsing.ui.common/src/org/simantics/browsing/ui/common/internal/GENodeQueryManager.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.browsing.ui.common / src / org / simantics / browsing / ui / common / internal / GENodeQueryManager.java
index bcbe40f9e2c2d4be9c3ac7336230d351dc628350..43b9d3bb253df135ceacc51c67e1ecb81a2d42f0 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.browsing.ui.common.internal;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Deque;\r
-import java.util.List;\r
-import java.util.Set;\r
-\r
-import org.simantics.browsing.ui.DataSource;\r
-import org.simantics.browsing.ui.NodeContext;\r
-import org.simantics.browsing.ui.NodeContext.CacheKey;\r
-import org.simantics.browsing.ui.NodeContext.PrimitiveQueryKey;\r
-import org.simantics.browsing.ui.NodeContext.QueryKey;\r
-import org.simantics.browsing.ui.NodeQueryManager;\r
-import org.simantics.browsing.ui.NodeQueryProcessor;\r
-import org.simantics.browsing.ui.PrimitiveQueryProcessor;\r
-import org.simantics.browsing.ui.PrimitiveQueryUpdater;\r
-import org.simantics.browsing.ui.common.internal.IGECache.IGECacheEntry;\r
-import org.simantics.browsing.ui.exception.NoDataSourceException;\r
-import org.simantics.browsing.ui.exception.NoQueryProcessorException;\r
-\r
-public class GENodeQueryManager implements NodeQueryManager, PrimitiveQueryUpdater {\r
-\r
-    private static final boolean DEBUG = false;\r
-\r
-    protected IGraphExplorerContext ge;\r
-    protected NodeContext parentContext;\r
-    protected CacheKey<?> parentKey;\r
-    protected UIElementReference treeReference;\r
-    protected boolean disposed = false;\r
-    List<GENodeQueryManager> children = new ArrayList<GENodeQueryManager>();\r
-\r
-    void indent() {\r
-        ge.queryIndent(1);\r
-    }\r
-\r
-    void unindent() {\r
-        ge.queryIndent(-1);\r
-    }\r
-\r
-    void queryDebug(String s) {\r
-        for (int i = 0; i < ge.queryIndent(); ++i)\r
-            System.err.append("   ");\r
-        System.err.println(s);\r
-    }\r
-\r
-    String toString(UIElementReference ref) {\r
-        return ref == null ? "" : ref.toString();\r
-    }\r
-\r
-    String toString(Set<UIElementReference> refs) {\r
-        if (refs == null || refs.isEmpty())\r
-            return "{}";\r
-        StringBuilder b = new StringBuilder();\r
-        for (UIElementReference ref : refs) {\r
-            b.append(toString(ref));\r
-        }\r
-        return b.toString();\r
-    }\r
-\r
-    String toString(NodeContext ctx, CacheKey<?> key) {\r
-        if (ctx == null)\r
-            return "null";\r
-        Set<UIElementReference> refs = ge.getCache().getTreeReference(ctx, key);\r
-        //return String.valueOf(System.identityHashCode(ctx)) + toString(ref);\r
-        //return String.valueOf(ctx.hashCode()) + ":" + String.valueOf(System.identityHashCode(ctx)) + toString(ref);\r
-        return ctx + toString(refs);\r
-    }\r
-\r
-\r
-    public GENodeQueryManager(GENodeQueryManager parent, NodeContext parentContext, CacheKey<?> parentKey, UIElementReference treeReference) {\r
-        this.ge = parent.ge;\r
-        this.parentContext = parentContext;\r
-        this.parentKey = parentKey;\r
-        this.treeReference = treeReference;\r
-        parent.children.add(this);\r
-    }\r
-    public GENodeQueryManager(IGraphExplorerContext ge, NodeContext parentContext, CacheKey<?> parentKey, UIElementReference treeReference) {\r
-        this.ge = ge;\r
-        this.parentContext = parentContext;\r
-        this.parentKey = parentKey;\r
-        this.treeReference = treeReference;\r
-    }\r
-\r
-    @Override\r
-    public Object getExplorerContext() {\r
-        return ge;\r
-    }\r
-    \r
-    public void dispose() {\r
-       if (disposed)\r
-               return;\r
-       disposed = true;\r
-       if (ge != null && parentContext != null && parentKey != null) {\r
-               ge.getCache().remove(parentContext, parentKey);\r
-       }\r
-       ge = null;\r
-       parentContext = null;\r
-       parentKey = null;\r
-       treeReference = null;\r
-       for (GENodeQueryManager m : children)\r
-               m.dispose();\r
-       children.clear();\r
-       children = null;\r
-    }\r
-\r
-//    @Override\r
-//    public String toString() {\r
-//        return "GENodeQueryManager[parentKey=" + parentKey + ", parentContext=" + "]";\r
-//    }\r
-//\r
-//    @Override\r
-//    public CacheKey getParentKey() {\r
-//        return parentKey;\r
-//    }\r
-//\r
-//    @Override\r
-//    public INodeContext getParentContext() {\r
-//        return parentContext;\r
-//    }\r
-\r
-//    static int koss = 0;\r
-    \r
-//    @Override\r
-    public <T> void replaceResult(NodeContext context, PrimitiveQueryKey<T> key, T newResult, int indent) {\r
-\r
-        IGraphExplorerContext ge = this.ge;\r
-       if (isDisposed())\r
-            return;\r
-       \r
-        if(DEBUG) {\r
-            queryDebug("replaceResult[" + ge.getCache().hashCode() + "] " + key + " -> " + newResult);\r
-            indent();\r
-        }\r
-        \r
-//        if((koss++ % 5000) == 0) {\r
-//            System.out.println("R" + koss);\r
-//        }\r
-        \r
-        IGECache cache = ge.getCache();\r
-        IGECacheEntry oldEntry = cache.getEntry(context, key);\r
-        if (oldEntry != null) {\r
-            cache.put(context, key, newResult);\r
-            propagate(context, key, oldEntry, indent);\r
-\r
-            Set<UIElementReference> refs = cache.removeTreeReference(context, key);\r
-\r
-            if (refs != null) {\r
-                //queryDebug("(replaceResult) found tree references " + toString(refs));\r
-                for (UIElementReference ref : refs)\r
-                    ge.update(ref);\r
-            }\r
-        } else {\r
-            // TODO: explain why this check is here or remove it!\r
-\r
-            // Consistency checking, no TreeReference should ever exist in this case!\r
-            Set<UIElementReference> ref = cache.getTreeReference(context, key);\r
-            assert ref == null;\r
-        }\r
-    }\r
-\r
-//    @Override\r
-    public <T> void clearResult(NodeContext context, CacheKey<T> key, int indent) {\r
-//        if (key == BuiltinKeys.FINAL_CHILDREN) {\r
-//            queryDebug("Clear final children for " + context);\r
-//        }\r
-        if(DEBUG) queryDebug("clearResult[" + ge.getCache().hashCode() + "] " + key + " " + context);\r
-\r
-        IGraphExplorerContext ge = this.ge;\r
-        if (isDisposed())\r
-            return;\r
-\r
-        IGECache cache = ge.getCache();\r
-        IGECacheEntry entry = cache.getEntry(context, key);\r
-        if (entry != null) {\r
-            cache.remove(context, key);\r
-            propagate(context, key, entry, indent);\r
-        }\r
-\r
-        Set<UIElementReference> refs = cache.removeTreeReference(context, key);\r
-        if (refs != null) {\r
-            //queryDebug("(clearResult) found tree reference " + toString(refs));\r
-            for (UIElementReference ref : refs)\r
-                ge.update(ref);\r
-        }\r
-    }\r
-\r
-    public <T> void propagate(NodeContext context, CacheKey<T> key, IGECacheEntry entry, int indent) {\r
-\r
-        if (isDisposed())\r
-            return;\r
-\r
-        if(DEBUG) queryDebug("propagate[" + ge.getCache().hashCode() + "] " + key + " - " + context);\r
-\r
-        assert entry != null;\r
-\r
-        for(IGECacheEntry dependency : entry.getDependencies()) {\r
-            clearResult(dependency.getContext(), dependency.getKey(), indent + 3);\r
-        }\r
-\r
-        entry.reset();\r
-    }\r
-\r
-    @SuppressWarnings("unchecked")\r
-    @Override\r
-    public <T> T query(NodeContext context, QueryKey<T> key) throws NoQueryProcessorException {\r
-\r
-        assert(!ge.isDisposed());\r
-\r
-        if(DEBUG) {\r
-            queryDebug("Query[" + ge.getCache().hashCode() + "] " + key + " " + toString(context, key) + " - " + parentKey + " " + toString(parentContext, parentKey));\r
-            indent();\r
-        }\r
-\r
-        assert(!(context == parentContext && key == parentKey));\r
-\r
-        assert(context != null);\r
-        assert(key != null);\r
-\r
-        T result = null;\r
-        IGECache cache = ge.getCache();\r
-\r
-        synchronized(ge.getPropagateLock()) {\r
-\r
-            IGECacheEntry entry = cache.getEntry(context, key);\r
-            //queryDebug("  CACHED RESULT: " + entry);\r
-            if(entry == null) {\r
-                entry = cache.put(context, key, null);\r
-                NodeQueryProcessor<T> processor = ge.getProcessor(key);\r
-                if(processor == null) {\r
-                    throw new NoQueryProcessorException(key);\r
-                }\r
-//                queryDebug("PERFORMING QUERY...");\r
-                T value = processor.query(new GENodeQueryManager(this, context, key, null), context);\r
-//                queryDebug("RESULT: " + value);\r
-                entry.setValue(value);\r
-            }\r
-\r
-            if(treeReference != null) {\r
-                UIElementReference cachedTreeReference = treeReference;\r
-\r
-                Set<UIElementReference> oldRefs = cache.getTreeReference(context, key);\r
-                if (oldRefs != null) {\r
-                    if (cachedTreeReference.isDisposed()) {\r
-                        oldRefs.remove(cachedTreeReference);\r
-                    } else {\r
-                        cache.putTreeReference(context, key, cachedTreeReference);\r
-                    }\r
-                } else {\r
-                    cache.putTreeReference(context, key, cachedTreeReference);\r
-                }\r
-            }\r
-\r
-            if(parentContext != null) {\r
-                assert(parentKey != null);\r
-                IGECacheEntry parentEntry = cache.getEntry(parentContext, parentKey);\r
-                if(parentEntry != null)\r
-                    entry.addDependency(parentEntry);\r
-            }\r
-\r
-            result = (T) entry.getValue();\r
-        }\r
-\r
-        unindent();\r
-        return result;\r
-    }\r
-\r
-    @SuppressWarnings("unchecked")\r
-    @Override\r
-    public <T> T query(NodeContext context, PrimitiveQueryKey<T> key) throws NoQueryProcessorException {\r
-\r
-        assert(!ge.isDisposed());\r
-\r
-        if(DEBUG) {\r
-            queryDebug("Primitive Query[" + ge.getCache().hashCode() + "] " + key + " " + toString(context, key) + " - " + parentKey + " " + toString(parentContext, key) + " " + Thread.currentThread().getName());\r
-            indent();\r
-        }\r
-\r
-        assert(!(context == parentContext && key == parentKey));\r
-\r
-        // Primitive queries must be leaf queries!\r
-        assert(!(parentKey instanceof PrimitiveQueryKey));\r
-\r
-        assert(context != null);\r
-        assert(key != null);\r
-\r
-        T result = null;\r
-\r
-        IGECache cache = ge.getCache();\r
-\r
-        synchronized(ge.getPropagateLock()) {\r
-            IGECacheEntry entry = cache.getEntry(context, key);\r
-            if(DEBUG) queryDebug("  CACHED PRIMITIVE RESULT[" + cache.hashCode() + "]: " + ((entry != null) ? (entry.hashCode() + "|" + System.identityHashCode(entry)) : 0));\r
-            if(entry == null) {\r
-                entry = cache.put(context, key, null);\r
-                PrimitiveQueryProcessor<T> processor = ge.getPrimitiveProcessor(key.processorIdenfitier());\r
-                if(processor == null) {\r
-                    throw new NoQueryProcessorException(key);\r
-                }\r
-//                queryDebug("PERFORMING PRIMITIVE QUERY...");\r
-                T value = processor.query(new GENodeQueryManager(this, context, key, null), context, key);\r
-//                queryDebug("PRIMITIVE RESULT: " + value);\r
-                entry.setValue(value);\r
-            }\r
-\r
-            if(treeReference != null) {\r
-                UIElementReference cachedTreeReference = treeReference;\r
-\r
-                Set<UIElementReference> oldRefs = cache.getTreeReference(context, key);\r
-                if (oldRefs != null) {\r
-                    if (cachedTreeReference.isDisposed()) {\r
-                        oldRefs.remove(cachedTreeReference);\r
-                    } else {\r
-                        cache.putTreeReference(context, key, cachedTreeReference);\r
-                    }\r
-                } else {\r
-                    cache.putTreeReference(context, key, cachedTreeReference);\r
-                }\r
-            }\r
-\r
-            if(parentContext != null) {\r
-                assert(parentKey != null);\r
-                IGECacheEntry parentEntry = cache.getEntry(parentContext, parentKey);\r
-                if(parentEntry != null) {\r
-                    entry.addDependency(parentEntry);\r
-                }\r
-            }\r
-\r
-            result = (T) entry.getValue();\r
-        }\r
-\r
-        unindent();\r
-        return result;\r
-    }\r
-\r
-    @Override\r
-    public <T> DataSource<T> tryGetDataSource(Class<T> clazz) {\r
-        return ge.getDataSource(clazz);\r
-    }\r
-\r
-    @Override\r
-    public <T> DataSource<T> getDataSource(Class<T> clazz) {\r
-        DataSource<T> dsp = ge.getDataSource(clazz);\r
-        if (dsp == null)\r
-            throw new NoDataSourceException(clazz);\r
-        return dsp;\r
-    }\r
-\r
-//    @Override\r
-//    public <T> void scheduleClear(final INodeContext context, final PrimitiveQueryKey<T> key) {\r
-//        ge.scheduler.execute(new Runnable() {\r
-//            @Override\r
-//            public void run() {\r
-//                synchronized(ge.propagate) {\r
-//                    clearResult(context, key, 0);\r
-//                }\r
-//            }\r
-//        });\r
-//    }\r
-\r
-\r
-//    @Override\r
-//    public <T> void create(final INodeContext context, final PrimitiveQueryKey<T> key, final T newResult) {\r
-//        ge.cache.put(context, key, newResult);\r
-//    }\r
-\r
-    @Override\r
-    public <T> void scheduleReplace(final NodeContext context, final PrimitiveQueryKey<T> key, final T newResult) {\r
-\r
-        if(DEBUG) queryDebug("scheduleReplace[" + ge.getCache().hashCode() + "] context=" + context + " key=" + key);\r
-\r
-        IGraphExplorerContext ge = this.ge;\r
-        if (isDisposed())\r
-            return;\r
-\r
-        class PropagateRunner implements Runnable {\r
-\r
-            @Override\r
-            public void run() {\r
-                IGraphExplorerContext ge = GENodeQueryManager.this.ge;\r
-                if (isDisposed())\r
-                    return;\r
-\r
-                int delay = 0;\r
-\r
-                List<Runnable> todo = null;\r
-                \r
-                synchronized(ge.getPropagateListLock()) {\r
-\r
-                    ge.setPropagating(true);\r
-\r
-                    List<Runnable> scheduleList = ge.getScheduleList();\r
-                    Deque<Integer> activity = ge.getActivity();\r
-\r
-                    activity.addFirst(scheduleList.size());\r
-                    activity.pollLast();\r
-\r
-                    int activityInt = 0;\r
-                    for(int i : activity) {\r
-                        activityInt += i;\r
-                    }\r
-                    ge.setActivityInt(activityInt);\r
-\r
-                    if(activityInt < 100) {\r
-                        delay = 10;\r
-                        //System.out.println("Scheduling propagate after 10ms.");\r
-                    } else if (activityInt < 1000) {\r
-                        delay = 500;\r
-                        //System.out.println("Scheduling propagate after 500ms.");\r
-                    } else {\r
-                        delay = 3000;\r
-                        //System.out.println("Scheduling propagate after 3000ms.");\r
-                    }\r
-                    \r
-                    todo = ge.getScheduleList();\r
-                    ge.setScheduleList(new ArrayList<Runnable>());\r
-\r
-                }\r
-\r
-                try {\r
-                    if(delay > 0)\r
-                        Thread.sleep(delay);\r
-                } catch (InterruptedException e) {\r
-                    e.printStackTrace();\r
-                }\r
-                if (isDisposed())\r
-                       return;\r
-                \r
-                synchronized(ge.getPropagateLock()) {\r
-\r
-                    for(Runnable r : todo) r.run();\r
-\r
-                }\r
-                if (isDisposed())\r
-                       return;\r
-                \r
-                synchronized(ge.getPropagateListLock()) {\r
-\r
-                    ge.setPropagating(false);\r
-\r
-                    if(!ge.getScheduleList().isEmpty())\r
-                        ge.scheduleQueryUpdate(new PropagateRunner());\r
-\r
-                }\r
-\r
-            }\r
-\r
-        }\r
-\r
-        synchronized(ge.getPropagateListLock()) {\r
-//            System.out.println("Schedule Replace: " + key + " - " + context);\r
-//            new Exception().printStackTrace();\r
-            List<Runnable> scheduleList = ge.getScheduleList();\r
-            scheduleList.add(new Runnable() {\r
-                @Override\r
-                public void run() {\r
-                    replaceResult(context, key, newResult, 0);\r
-                }\r
-            });\r
-\r
-            if(ge.isPropagating()) return;\r
-\r
-            ge.scheduleQueryUpdate(new PropagateRunner());\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public boolean isDisposed() {\r
-        if (disposed)\r
-               return true;\r
-        if (ge.isDisposed()) {\r
-               dispose();\r
-               return true;\r
-        }\r
-        return false;\r
-    }\r
-    \r
-    @Override\r
-    public boolean isShown(NodeContext context) {\r
-        IGraphExplorerContext ge = this.ge;\r
-       if (isDisposed())\r
-               return false;\r
-       return ge.getCache().isShown(context);\r
-    }\r
-\r
-    @Override\r
-    public void incRef(NodeContext context) {\r
-        IGraphExplorerContext ge = this.ge;\r
-       if (isDisposed())\r
-               return;\r
-       ge.getCache().incRef(context);\r
-    }\r
-    \r
-    @Override\r
-    public void decRef(NodeContext context) {\r
-        IGraphExplorerContext ge = this.ge;\r
-       if (isDisposed())\r
-               return;\r
-       ge.getCache().decRef(context);\r
-    }\r
-    \r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.browsing.ui.common.internal;
+
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.Set;
+
+import org.simantics.browsing.ui.DataSource;
+import org.simantics.browsing.ui.NodeContext;
+import org.simantics.browsing.ui.NodeContext.CacheKey;
+import org.simantics.browsing.ui.NodeContext.PrimitiveQueryKey;
+import org.simantics.browsing.ui.NodeContext.QueryKey;
+import org.simantics.browsing.ui.NodeQueryManager;
+import org.simantics.browsing.ui.NodeQueryProcessor;
+import org.simantics.browsing.ui.PrimitiveQueryProcessor;
+import org.simantics.browsing.ui.PrimitiveQueryUpdater;
+import org.simantics.browsing.ui.common.internal.IGECache.IGECacheEntry;
+import org.simantics.browsing.ui.exception.NoDataSourceException;
+import org.simantics.browsing.ui.exception.NoQueryProcessorException;
+
+public class GENodeQueryManager implements NodeQueryManager, PrimitiveQueryUpdater {
+
+    private static final boolean DEBUG = false;
+
+    protected IGraphExplorerContext ge;
+    protected NodeContext parentContext;
+    protected CacheKey<?> parentKey;
+    protected UIElementReference treeReference;
+    protected boolean disposed = false;
+    List<GENodeQueryManager> children = new ArrayList<GENodeQueryManager>();
+
+    void indent() {
+        ge.queryIndent(1);
+    }
+
+    void unindent() {
+        ge.queryIndent(-1);
+    }
+
+    void queryDebug(String s) {
+        for (int i = 0; i < ge.queryIndent(); ++i)
+            System.err.append("   ");
+        System.err.println(s);
+    }
+
+    String toString(UIElementReference ref) {
+        return ref == null ? "" : ref.toString();
+    }
+
+    String toString(Set<UIElementReference> refs) {
+        if (refs == null || refs.isEmpty())
+            return "{}";
+        StringBuilder b = new StringBuilder();
+        for (UIElementReference ref : refs) {
+            b.append(toString(ref));
+        }
+        return b.toString();
+    }
+
+    String toString(NodeContext ctx, CacheKey<?> key) {
+        if (ctx == null)
+            return "null";
+        Set<UIElementReference> refs = ge.getCache().getTreeReference(ctx, key);
+        //return String.valueOf(System.identityHashCode(ctx)) + toString(ref);
+        //return String.valueOf(ctx.hashCode()) + ":" + String.valueOf(System.identityHashCode(ctx)) + toString(ref);
+        return ctx + toString(refs);
+    }
+
+
+    public GENodeQueryManager(GENodeQueryManager parent, NodeContext parentContext, CacheKey<?> parentKey, UIElementReference treeReference) {
+        this.ge = parent.ge;
+        this.parentContext = parentContext;
+        this.parentKey = parentKey;
+        this.treeReference = treeReference;
+        parent.children.add(this);
+    }
+    public GENodeQueryManager(IGraphExplorerContext ge, NodeContext parentContext, CacheKey<?> parentKey, UIElementReference treeReference) {
+        this.ge = ge;
+        this.parentContext = parentContext;
+        this.parentKey = parentKey;
+        this.treeReference = treeReference;
+    }
+
+    @Override
+    public Object getExplorerContext() {
+        return ge;
+    }
+    
+    public void dispose() {
+       if (disposed)
+               return;
+       disposed = true;
+       if (ge != null && parentContext != null && parentKey != null) {
+               ge.getCache().remove(parentContext, parentKey);
+       }
+       ge = null;
+       parentContext = null;
+       parentKey = null;
+       treeReference = null;
+       for (GENodeQueryManager m : children)
+               m.dispose();
+       children.clear();
+       children = null;
+    }
+
+//    @Override
+//    public String toString() {
+//        return "GENodeQueryManager[parentKey=" + parentKey + ", parentContext=" + "]";
+//    }
+//
+//    @Override
+//    public CacheKey getParentKey() {
+//        return parentKey;
+//    }
+//
+//    @Override
+//    public INodeContext getParentContext() {
+//        return parentContext;
+//    }
+
+//    static int koss = 0;
+    
+//    @Override
+    public <T> void replaceResult(NodeContext context, PrimitiveQueryKey<T> key, T newResult, int indent) {
+
+        IGraphExplorerContext ge = this.ge;
+       if (isDisposed())
+            return;
+       
+        if(DEBUG) {
+            queryDebug("replaceResult[" + ge.getCache().hashCode() + "] " + key + " -> " + newResult);
+            indent();
+        }
+        
+//        if((koss++ % 5000) == 0) {
+//            System.out.println("R" + koss);
+//        }
+        
+        IGECache cache = ge.getCache();
+        IGECacheEntry oldEntry = cache.getEntry(context, key);
+        if (oldEntry != null) {
+            cache.put(context, key, newResult);
+            propagate(context, key, oldEntry, indent);
+
+            Set<UIElementReference> refs = cache.removeTreeReference(context, key);
+
+            if (refs != null) {
+                //queryDebug("(replaceResult) found tree references " + toString(refs));
+                for (UIElementReference ref : refs)
+                    ge.update(ref);
+            }
+        } else {
+            // TODO: explain why this check is here or remove it!
+
+            // Consistency checking, no TreeReference should ever exist in this case!
+            Set<UIElementReference> ref = cache.getTreeReference(context, key);
+            assert ref == null;
+        }
+    }
+
+//    @Override
+    public <T> void clearResult(NodeContext context, CacheKey<T> key, int indent) {
+//        if (key == BuiltinKeys.FINAL_CHILDREN) {
+//            queryDebug("Clear final children for " + context);
+//        }
+        if(DEBUG) queryDebug("clearResult[" + ge.getCache().hashCode() + "] " + key + " " + context);
+
+        IGraphExplorerContext ge = this.ge;
+        if (isDisposed())
+            return;
+
+        IGECache cache = ge.getCache();
+        IGECacheEntry entry = cache.getEntry(context, key);
+        if (entry != null) {
+            cache.remove(context, key);
+            propagate(context, key, entry, indent);
+        }
+
+        Set<UIElementReference> refs = cache.removeTreeReference(context, key);
+        if (refs != null) {
+            //queryDebug("(clearResult) found tree reference " + toString(refs));
+            for (UIElementReference ref : refs)
+                ge.update(ref);
+        }
+    }
+
+    public <T> void propagate(NodeContext context, CacheKey<T> key, IGECacheEntry entry, int indent) {
+
+        if (isDisposed())
+            return;
+
+        if(DEBUG) queryDebug("propagate[" + ge.getCache().hashCode() + "] " + key + " - " + context);
+
+        assert entry != null;
+
+        for(IGECacheEntry dependency : entry.getDependencies()) {
+            clearResult(dependency.getContext(), dependency.getKey(), indent + 3);
+        }
+
+        entry.reset();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T query(NodeContext context, QueryKey<T> key) throws NoQueryProcessorException {
+
+        assert(!ge.isDisposed());
+
+        if(DEBUG) {
+            queryDebug("Query[" + ge.getCache().hashCode() + "] " + key + " " + toString(context, key) + " - " + parentKey + " " + toString(parentContext, parentKey));
+            indent();
+        }
+
+        assert(!(context == parentContext && key == parentKey));
+
+        assert(context != null);
+        assert(key != null);
+
+        T result = null;
+        IGECache cache = ge.getCache();
+
+        synchronized(ge.getPropagateLock()) {
+
+            IGECacheEntry entry = cache.getEntry(context, key);
+            //queryDebug("  CACHED RESULT: " + entry);
+            if(entry == null) {
+                entry = cache.put(context, key, null);
+                NodeQueryProcessor<T> processor = ge.getProcessor(key);
+                if(processor == null) {
+                    throw new NoQueryProcessorException(key);
+                }
+//                queryDebug("PERFORMING QUERY...");
+                T value = processor.query(new GENodeQueryManager(this, context, key, null), context);
+//                queryDebug("RESULT: " + value);
+                entry.setValue(value);
+            }
+
+            if(treeReference != null) {
+                UIElementReference cachedTreeReference = treeReference;
+
+                Set<UIElementReference> oldRefs = cache.getTreeReference(context, key);
+                if (oldRefs != null) {
+                    if (cachedTreeReference.isDisposed()) {
+                        oldRefs.remove(cachedTreeReference);
+                    } else {
+                        cache.putTreeReference(context, key, cachedTreeReference);
+                    }
+                } else {
+                    cache.putTreeReference(context, key, cachedTreeReference);
+                }
+            }
+
+            if(parentContext != null) {
+                assert(parentKey != null);
+                IGECacheEntry parentEntry = cache.getEntry(parentContext, parentKey);
+                if(parentEntry != null)
+                    entry.addDependency(parentEntry);
+            }
+
+            result = (T) entry.getValue();
+        }
+
+        unindent();
+        return result;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T query(NodeContext context, PrimitiveQueryKey<T> key) throws NoQueryProcessorException {
+
+        assert(!ge.isDisposed());
+
+        if(DEBUG) {
+            queryDebug("Primitive Query[" + ge.getCache().hashCode() + "] " + key + " " + toString(context, key) + " - " + parentKey + " " + toString(parentContext, key) + " " + Thread.currentThread().getName());
+            indent();
+        }
+
+        assert(!(context == parentContext && key == parentKey));
+
+        // Primitive queries must be leaf queries!
+        assert(!(parentKey instanceof PrimitiveQueryKey));
+
+        assert(context != null);
+        assert(key != null);
+
+        T result = null;
+
+        IGECache cache = ge.getCache();
+
+        synchronized(ge.getPropagateLock()) {
+            IGECacheEntry entry = cache.getEntry(context, key);
+            if(DEBUG) queryDebug("  CACHED PRIMITIVE RESULT[" + cache.hashCode() + "]: " + ((entry != null) ? (entry.hashCode() + "|" + System.identityHashCode(entry)) : 0));
+            if(entry == null) {
+                entry = cache.put(context, key, null);
+                PrimitiveQueryProcessor<T> processor = ge.getPrimitiveProcessor(key.processorIdenfitier());
+                if(processor == null) {
+                    throw new NoQueryProcessorException(key);
+                }
+//                queryDebug("PERFORMING PRIMITIVE QUERY...");
+                T value = processor.query(new GENodeQueryManager(this, context, key, null), context, key);
+//                queryDebug("PRIMITIVE RESULT: " + value);
+                entry.setValue(value);
+            }
+
+            if(treeReference != null) {
+                UIElementReference cachedTreeReference = treeReference;
+
+                Set<UIElementReference> oldRefs = cache.getTreeReference(context, key);
+                if (oldRefs != null) {
+                    if (cachedTreeReference.isDisposed()) {
+                        oldRefs.remove(cachedTreeReference);
+                    } else {
+                        cache.putTreeReference(context, key, cachedTreeReference);
+                    }
+                } else {
+                    cache.putTreeReference(context, key, cachedTreeReference);
+                }
+            }
+
+            if(parentContext != null) {
+                assert(parentKey != null);
+                IGECacheEntry parentEntry = cache.getEntry(parentContext, parentKey);
+                if(parentEntry != null) {
+                    entry.addDependency(parentEntry);
+                }
+            }
+
+            result = (T) entry.getValue();
+        }
+
+        unindent();
+        return result;
+    }
+
+    @Override
+    public <T> DataSource<T> tryGetDataSource(Class<T> clazz) {
+        return ge.getDataSource(clazz);
+    }
+
+    @Override
+    public <T> DataSource<T> getDataSource(Class<T> clazz) {
+        DataSource<T> dsp = ge.getDataSource(clazz);
+        if (dsp == null)
+            throw new NoDataSourceException(clazz);
+        return dsp;
+    }
+
+//    @Override
+//    public <T> void scheduleClear(final INodeContext context, final PrimitiveQueryKey<T> key) {
+//        ge.scheduler.execute(new Runnable() {
+//            @Override
+//            public void run() {
+//                synchronized(ge.propagate) {
+//                    clearResult(context, key, 0);
+//                }
+//            }
+//        });
+//    }
+
+
+//    @Override
+//    public <T> void create(final INodeContext context, final PrimitiveQueryKey<T> key, final T newResult) {
+//        ge.cache.put(context, key, newResult);
+//    }
+
+    @Override
+    public <T> void scheduleReplace(final NodeContext context, final PrimitiveQueryKey<T> key, final T newResult) {
+
+        if(DEBUG) queryDebug("scheduleReplace[" + ge.getCache().hashCode() + "] context=" + context + " key=" + key);
+
+        IGraphExplorerContext ge = this.ge;
+        if (isDisposed())
+            return;
+
+        class PropagateRunner implements Runnable {
+
+            @Override
+            public void run() {
+                IGraphExplorerContext ge = GENodeQueryManager.this.ge;
+                if (isDisposed())
+                    return;
+
+                int delay = 0;
+
+                List<Runnable> todo = null;
+                
+                synchronized(ge.getPropagateListLock()) {
+
+                    ge.setPropagating(true);
+
+                    List<Runnable> scheduleList = ge.getScheduleList();
+                    Deque<Integer> activity = ge.getActivity();
+
+                    activity.addFirst(scheduleList.size());
+                    activity.pollLast();
+
+                    int activityInt = 0;
+                    for(int i : activity) {
+                        activityInt += i;
+                    }
+                    ge.setActivityInt(activityInt);
+
+                    if(activityInt < 100) {
+                        delay = 10;
+                        //System.out.println("Scheduling propagate after 10ms.");
+                    } else if (activityInt < 1000) {
+                        delay = 500;
+                        //System.out.println("Scheduling propagate after 500ms.");
+                    } else {
+                        delay = 3000;
+                        //System.out.println("Scheduling propagate after 3000ms.");
+                    }
+                    
+                    todo = ge.getScheduleList();
+                    ge.setScheduleList(new ArrayList<Runnable>());
+
+                }
+
+                try {
+                    if(delay > 0)
+                        Thread.sleep(delay);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                if (isDisposed())
+                       return;
+                
+                synchronized(ge.getPropagateLock()) {
+
+                    for(Runnable r : todo) r.run();
+
+                }
+                if (isDisposed())
+                       return;
+                
+                synchronized(ge.getPropagateListLock()) {
+
+                    ge.setPropagating(false);
+
+                    if(!ge.getScheduleList().isEmpty())
+                        ge.scheduleQueryUpdate(new PropagateRunner());
+
+                }
+
+            }
+
+        }
+
+        synchronized(ge.getPropagateListLock()) {
+//            System.out.println("Schedule Replace: " + key + " - " + context);
+//            new Exception().printStackTrace();
+            List<Runnable> scheduleList = ge.getScheduleList();
+            scheduleList.add(new Runnable() {
+                @Override
+                public void run() {
+                    replaceResult(context, key, newResult, 0);
+                }
+            });
+
+            if(ge.isPropagating()) return;
+
+            ge.scheduleQueryUpdate(new PropagateRunner());
+        }
+    }
+
+    @Override
+    public boolean isDisposed() {
+        if (disposed)
+               return true;
+        if (ge.isDisposed()) {
+               dispose();
+               return true;
+        }
+        return false;
+    }
+    
+    @Override
+    public boolean isShown(NodeContext context) {
+        IGraphExplorerContext ge = this.ge;
+       if (isDisposed())
+               return false;
+       return ge.getCache().isShown(context);
+    }
+
+    @Override
+    public void incRef(NodeContext context) {
+        IGraphExplorerContext ge = this.ge;
+       if (isDisposed())
+               return;
+       ge.getCache().incRef(context);
+    }
+    
+    @Override
+    public void decRef(NodeContext context) {
+        IGraphExplorerContext ge = this.ge;
+       if (isDisposed())
+               return;
+       ge.getCache().decRef(context);
+    }
+    
+}