--- /dev/null
+/*******************************************************************************\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