1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.browsing.ui.common.internal;
14 import java.util.ArrayList;
15 import java.util.Deque;
16 import java.util.List;
19 import org.simantics.browsing.ui.DataSource;
20 import org.simantics.browsing.ui.NodeContext;
21 import org.simantics.browsing.ui.NodeContext.CacheKey;
22 import org.simantics.browsing.ui.NodeContext.PrimitiveQueryKey;
23 import org.simantics.browsing.ui.NodeContext.QueryKey;
24 import org.simantics.browsing.ui.NodeQueryManager;
25 import org.simantics.browsing.ui.NodeQueryProcessor;
26 import org.simantics.browsing.ui.PrimitiveQueryProcessor;
27 import org.simantics.browsing.ui.PrimitiveQueryUpdater;
28 import org.simantics.browsing.ui.common.internal.IGECache.IGECacheEntry;
29 import org.simantics.browsing.ui.exception.NoDataSourceException;
30 import org.simantics.browsing.ui.exception.NoQueryProcessorException;
31 import org.slf4j.LoggerFactory;
33 public class GENodeQueryManager implements NodeQueryManager, PrimitiveQueryUpdater {
35 private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(GENodeQueryManager.class);
36 private static final boolean DEBUG = false;
38 protected IGraphExplorerContext ge;
39 protected NodeContext parentContext;
40 protected CacheKey<?> parentKey;
41 protected UIElementReference treeReference;
42 protected boolean disposed = false;
43 List<GENodeQueryManager> children = new ArrayList<GENodeQueryManager>();
53 void queryDebug(String s) {
54 for (int i = 0; i < ge.queryIndent(); ++i)
59 String toString(UIElementReference ref) {
60 return ref == null ? "" : ref.toString();
63 String toString(Set<UIElementReference> refs) {
64 if (refs == null || refs.isEmpty())
66 StringBuilder b = new StringBuilder();
67 for (UIElementReference ref : refs) {
68 b.append(toString(ref));
73 String toString(NodeContext ctx, CacheKey<?> key) {
76 Set<UIElementReference> refs = ge.getCache().getTreeReference(ctx, key);
77 //return String.valueOf(System.identityHashCode(ctx)) + toString(ref);
78 //return String.valueOf(ctx.hashCode()) + ":" + String.valueOf(System.identityHashCode(ctx)) + toString(ref);
79 return ctx + toString(refs);
83 public GENodeQueryManager(GENodeQueryManager parent, NodeContext parentContext, CacheKey<?> parentKey, UIElementReference treeReference) {
85 this.parentContext = parentContext;
86 this.parentKey = parentKey;
87 this.treeReference = treeReference;
88 parent.children.add(this);
90 public GENodeQueryManager(IGraphExplorerContext ge, NodeContext parentContext, CacheKey<?> parentKey, UIElementReference treeReference) {
92 this.parentContext = parentContext;
93 this.parentKey = parentKey;
94 this.treeReference = treeReference;
98 public Object getExplorerContext() {
102 public void dispose() {
106 if (ge != null && parentContext != null && parentKey != null) {
107 ge.getCache().remove(parentContext, parentKey);
110 parentContext = null;
112 treeReference = null;
113 for (GENodeQueryManager m : children)
120 // public String toString() {
121 // return "GENodeQueryManager[parentKey=" + parentKey + ", parentContext=" + "]";
125 // public CacheKey getParentKey() {
130 // public INodeContext getParentContext() {
131 // return parentContext;
134 // static int koss = 0;
137 public <T> void replaceResult(NodeContext context, PrimitiveQueryKey<T> key, T newResult, int indent) {
139 IGraphExplorerContext ge = this.ge;
144 queryDebug("replaceResult[" + ge.getCache().hashCode() + "] " + key + " -> " + newResult);
148 // if((koss++ % 5000) == 0) {
149 // System.out.println("R" + koss);
152 IGECache cache = ge.getCache();
153 IGECacheEntry oldEntry = cache.getEntry(context, key);
154 if (oldEntry != null) {
155 cache.put(context, key, newResult);
156 propagate(context, key, oldEntry, indent);
158 Set<UIElementReference> refs = cache.removeTreeReference(context, key);
161 //queryDebug("(replaceResult) found tree references " + toString(refs));
162 for (UIElementReference ref : refs)
166 // TODO: explain why this check is here or remove it!
168 // Consistency checking, no TreeReference should ever exist in this case!
169 Set<UIElementReference> ref = cache.getTreeReference(context, key);
175 public <T> void clearResult(NodeContext context, CacheKey<T> key, int indent) {
176 // if (key == BuiltinKeys.FINAL_CHILDREN) {
177 // queryDebug("Clear final children for " + context);
179 if(DEBUG) queryDebug("clearResult[" + ge.getCache().hashCode() + "] " + key + " " + context);
181 IGraphExplorerContext ge = this.ge;
185 IGECache cache = ge.getCache();
186 IGECacheEntry entry = cache.getEntry(context, key);
188 cache.remove(context, key);
189 propagate(context, key, entry, indent);
192 Set<UIElementReference> refs = cache.removeTreeReference(context, key);
194 //queryDebug("(clearResult) found tree reference " + toString(refs));
195 for (UIElementReference ref : refs)
200 public <T> void propagate(NodeContext context, CacheKey<T> key, IGECacheEntry entry, int indent) {
205 if(DEBUG) queryDebug("propagate[" + ge.getCache().hashCode() + "] " + key + " - " + context);
207 assert entry != null;
209 for(IGECacheEntry dependency : entry.getDependencies()) {
210 clearResult(dependency.getContext(), dependency.getKey(), indent + 3);
216 @SuppressWarnings("unchecked")
218 public <T> T query(NodeContext context, QueryKey<T> key) throws NoQueryProcessorException {
220 assert(!ge.isDisposed());
223 queryDebug("Query[" + ge.getCache().hashCode() + "] " + key + " " + toString(context, key) + " - " + parentKey + " " + toString(parentContext, parentKey));
227 assert(!(context == parentContext && key == parentKey));
229 assert(context != null);
233 IGECache cache = ge.getCache();
235 synchronized(ge.getPropagateLock()) {
237 IGECacheEntry entry = cache.getEntry(context, key);
238 //queryDebug(" CACHED RESULT: " + entry);
240 entry = cache.put(context, key, null);
241 NodeQueryProcessor<T> processor = ge.getProcessor(key);
242 if(processor == null) {
243 throw new NoQueryProcessorException(key);
245 // queryDebug("PERFORMING QUERY...");
246 T value = processor.query(new GENodeQueryManager(this, context, key, null), context);
247 // queryDebug("RESULT: " + value);
248 entry.setValue(value);
251 if(treeReference != null) {
252 UIElementReference cachedTreeReference = treeReference;
254 Set<UIElementReference> oldRefs = cache.getTreeReference(context, key);
255 if (oldRefs != null) {
256 if (cachedTreeReference.isDisposed()) {
257 oldRefs.remove(cachedTreeReference);
259 cache.putTreeReference(context, key, cachedTreeReference);
262 cache.putTreeReference(context, key, cachedTreeReference);
266 if(parentContext != null) {
267 assert(parentKey != null);
268 IGECacheEntry parentEntry = cache.getEntry(parentContext, parentKey);
269 if(parentEntry != null)
270 entry.addDependency(parentEntry);
273 result = (T) entry.getValue();
280 @SuppressWarnings("unchecked")
282 public <T> T query(NodeContext context, PrimitiveQueryKey<T> key) throws NoQueryProcessorException {
284 assert(!ge.isDisposed());
287 queryDebug("Primitive Query[" + ge.getCache().hashCode() + "] " + key + " " + toString(context, key) + " - " + parentKey + " " + toString(parentContext, key) + " " + Thread.currentThread().getName());
291 assert(!(context == parentContext && key == parentKey));
293 // Primitive queries must be leaf queries!
294 assert(!(parentKey instanceof PrimitiveQueryKey));
296 assert(context != null);
301 IGECache cache = ge.getCache();
303 synchronized(ge.getPropagateLock()) {
304 IGECacheEntry entry = cache.getEntry(context, key);
305 if(DEBUG) queryDebug(" CACHED PRIMITIVE RESULT[" + cache.hashCode() + "]: " + ((entry != null) ? (entry.hashCode() + "|" + System.identityHashCode(entry)) : 0));
307 entry = cache.put(context, key, null);
308 PrimitiveQueryProcessor<T> processor = ge.getPrimitiveProcessor(key.processorIdenfitier());
309 if(processor == null) {
310 throw new NoQueryProcessorException(key);
312 // queryDebug("PERFORMING PRIMITIVE QUERY...");
313 T value = processor.query(new GENodeQueryManager(this, context, key, null), context, key);
314 // queryDebug("PRIMITIVE RESULT: " + value);
315 entry.setValue(value);
318 if(treeReference != null) {
319 UIElementReference cachedTreeReference = treeReference;
321 Set<UIElementReference> oldRefs = cache.getTreeReference(context, key);
322 if (oldRefs != null) {
323 if (cachedTreeReference.isDisposed()) {
324 oldRefs.remove(cachedTreeReference);
326 cache.putTreeReference(context, key, cachedTreeReference);
329 cache.putTreeReference(context, key, cachedTreeReference);
333 if(parentContext != null) {
334 assert(parentKey != null);
335 IGECacheEntry parentEntry = cache.getEntry(parentContext, parentKey);
336 if(parentEntry != null) {
337 entry.addDependency(parentEntry);
341 result = (T) entry.getValue();
349 public <T> DataSource<T> tryGetDataSource(Class<T> clazz) {
350 return ge.getDataSource(clazz);
354 public <T> DataSource<T> getDataSource(Class<T> clazz) {
355 DataSource<T> dsp = ge.getDataSource(clazz);
357 throw new NoDataSourceException(clazz);
362 // public <T> void scheduleClear(final INodeContext context, final PrimitiveQueryKey<T> key) {
363 // ge.scheduler.execute(new Runnable() {
365 // public void run() {
366 // synchronized(ge.propagate) {
367 // clearResult(context, key, 0);
375 // public <T> void create(final INodeContext context, final PrimitiveQueryKey<T> key, final T newResult) {
376 // ge.cache.put(context, key, newResult);
380 public <T> void scheduleReplace(final NodeContext context, final PrimitiveQueryKey<T> key, final T newResult) {
382 if(DEBUG) queryDebug("scheduleReplace[" + ge.getCache().hashCode() + "] context=" + context + " key=" + key);
384 IGraphExplorerContext ge = this.ge;
388 class PropagateRunner implements Runnable {
392 IGraphExplorerContext ge = GENodeQueryManager.this.ge;
398 List<Runnable> todo = null;
400 synchronized(ge.getPropagateListLock()) {
402 ge.setPropagating(true);
404 List<Runnable> scheduleList = ge.getScheduleList();
405 Deque<Integer> activity = ge.getActivity();
407 activity.addFirst(scheduleList.size());
411 for(int i : activity) {
414 ge.setActivityInt(activityInt);
416 if(activityInt < 100) {
418 //System.out.println("Scheduling propagate after 10ms.");
419 } else if (activityInt < 1000) {
421 //System.out.println("Scheduling propagate after 500ms.");
424 //System.out.println("Scheduling propagate after 3000ms.");
427 todo = ge.getScheduleList();
428 ge.setScheduleList(new ArrayList<Runnable>());
435 } catch (InterruptedException e) {
441 synchronized(ge.getPropagateLock()) {
443 for(Runnable r : todo) r.run();
449 synchronized(ge.getPropagateListLock()) {
451 ge.setPropagating(false);
453 if(!ge.getScheduleList().isEmpty())
454 ge.scheduleQueryUpdate(new PropagateRunner());
462 synchronized(ge.getPropagateListLock()) {
463 // System.out.println("Schedule Replace: " + key + " - " + context);
464 // new Exception().printStackTrace();
465 List<Runnable> scheduleList = ge.getScheduleList();
466 scheduleList.add(new Runnable() {
469 replaceResult(context, key, newResult, 0);
473 if(ge.isPropagating()) return;
475 ge.scheduleQueryUpdate(new PropagateRunner());
480 public boolean isDisposed() {
483 if (ge.isDisposed()) {
491 public boolean isShown(NodeContext context) {
492 IGraphExplorerContext ge = this.ge;
495 return ge.getCache().isShown(context);
499 public void incRef(NodeContext context) {
500 IGraphExplorerContext ge = this.ge;
503 ge.getCache().incRef(context);
507 public void decRef(NodeContext context) {
508 IGraphExplorerContext ge = this.ge;
511 ge.getCache().decRef(context);
514 public void execFromQuery(Runnable runnable) {
515 ge.execFromQuery(runnable);