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;
32 public class GENodeQueryManager implements NodeQueryManager, PrimitiveQueryUpdater {
34 private static final boolean DEBUG = false;
36 protected IGraphExplorerContext ge;
37 protected NodeContext parentContext;
38 protected CacheKey<?> parentKey;
39 protected UIElementReference treeReference;
40 protected boolean disposed = false;
41 List<GENodeQueryManager> children = new ArrayList<GENodeQueryManager>();
51 void queryDebug(String s) {
52 for (int i = 0; i < ge.queryIndent(); ++i)
53 System.err.append(" ");
54 System.err.println(s);
57 String toString(UIElementReference ref) {
58 return ref == null ? "" : ref.toString();
61 String toString(Set<UIElementReference> refs) {
62 if (refs == null || refs.isEmpty())
64 StringBuilder b = new StringBuilder();
65 for (UIElementReference ref : refs) {
66 b.append(toString(ref));
71 String toString(NodeContext ctx, CacheKey<?> key) {
74 Set<UIElementReference> refs = ge.getCache().getTreeReference(ctx, key);
75 //return String.valueOf(System.identityHashCode(ctx)) + toString(ref);
76 //return String.valueOf(ctx.hashCode()) + ":" + String.valueOf(System.identityHashCode(ctx)) + toString(ref);
77 return ctx + toString(refs);
81 public GENodeQueryManager(GENodeQueryManager parent, NodeContext parentContext, CacheKey<?> parentKey, UIElementReference treeReference) {
83 this.parentContext = parentContext;
84 this.parentKey = parentKey;
85 this.treeReference = treeReference;
86 parent.children.add(this);
88 public GENodeQueryManager(IGraphExplorerContext ge, NodeContext parentContext, CacheKey<?> parentKey, UIElementReference treeReference) {
90 this.parentContext = parentContext;
91 this.parentKey = parentKey;
92 this.treeReference = treeReference;
96 public Object getExplorerContext() {
100 public void dispose() {
104 if (ge != null && parentContext != null && parentKey != null) {
105 ge.getCache().remove(parentContext, parentKey);
108 parentContext = null;
110 treeReference = null;
111 for (GENodeQueryManager m : children)
118 // public String toString() {
119 // return "GENodeQueryManager[parentKey=" + parentKey + ", parentContext=" + "]";
123 // public CacheKey getParentKey() {
128 // public INodeContext getParentContext() {
129 // return parentContext;
132 // static int koss = 0;
135 public <T> void replaceResult(NodeContext context, PrimitiveQueryKey<T> key, T newResult, int indent) {
137 IGraphExplorerContext ge = this.ge;
142 queryDebug("replaceResult[" + ge.getCache().hashCode() + "] " + key + " -> " + newResult);
146 // if((koss++ % 5000) == 0) {
147 // System.out.println("R" + koss);
150 IGECache cache = ge.getCache();
151 IGECacheEntry oldEntry = cache.getEntry(context, key);
152 if (oldEntry != null) {
153 cache.put(context, key, newResult);
154 propagate(context, key, oldEntry, indent);
156 Set<UIElementReference> refs = cache.removeTreeReference(context, key);
159 //queryDebug("(replaceResult) found tree references " + toString(refs));
160 for (UIElementReference ref : refs)
164 // TODO: explain why this check is here or remove it!
166 // Consistency checking, no TreeReference should ever exist in this case!
167 Set<UIElementReference> ref = cache.getTreeReference(context, key);
173 public <T> void clearResult(NodeContext context, CacheKey<T> key, int indent) {
174 // if (key == BuiltinKeys.FINAL_CHILDREN) {
175 // queryDebug("Clear final children for " + context);
177 if(DEBUG) queryDebug("clearResult[" + ge.getCache().hashCode() + "] " + key + " " + context);
179 IGraphExplorerContext ge = this.ge;
183 IGECache cache = ge.getCache();
184 IGECacheEntry entry = cache.getEntry(context, key);
186 cache.remove(context, key);
187 propagate(context, key, entry, indent);
190 Set<UIElementReference> refs = cache.removeTreeReference(context, key);
192 //queryDebug("(clearResult) found tree reference " + toString(refs));
193 for (UIElementReference ref : refs)
198 public <T> void propagate(NodeContext context, CacheKey<T> key, IGECacheEntry entry, int indent) {
203 if(DEBUG) queryDebug("propagate[" + ge.getCache().hashCode() + "] " + key + " - " + context);
205 assert entry != null;
207 for(IGECacheEntry dependency : entry.getDependencies()) {
208 clearResult(dependency.getContext(), dependency.getKey(), indent + 3);
214 @SuppressWarnings("unchecked")
216 public <T> T query(NodeContext context, QueryKey<T> key) throws NoQueryProcessorException {
218 assert(!ge.isDisposed());
221 queryDebug("Query[" + ge.getCache().hashCode() + "] " + key + " " + toString(context, key) + " - " + parentKey + " " + toString(parentContext, parentKey));
225 assert(!(context == parentContext && key == parentKey));
227 assert(context != null);
231 IGECache cache = ge.getCache();
233 synchronized(ge.getPropagateLock()) {
235 IGECacheEntry entry = cache.getEntry(context, key);
236 //queryDebug(" CACHED RESULT: " + entry);
238 entry = cache.put(context, key, null);
239 NodeQueryProcessor<T> processor = ge.getProcessor(key);
240 if(processor == null) {
241 throw new NoQueryProcessorException(key);
243 // queryDebug("PERFORMING QUERY...");
244 T value = processor.query(new GENodeQueryManager(this, context, key, null), context);
245 // queryDebug("RESULT: " + value);
246 entry.setValue(value);
249 if(treeReference != null) {
250 UIElementReference cachedTreeReference = treeReference;
252 Set<UIElementReference> oldRefs = cache.getTreeReference(context, key);
253 if (oldRefs != null) {
254 if (cachedTreeReference.isDisposed()) {
255 oldRefs.remove(cachedTreeReference);
257 cache.putTreeReference(context, key, cachedTreeReference);
260 cache.putTreeReference(context, key, cachedTreeReference);
264 if(parentContext != null) {
265 assert(parentKey != null);
266 IGECacheEntry parentEntry = cache.getEntry(parentContext, parentKey);
267 if(parentEntry != null)
268 entry.addDependency(parentEntry);
271 result = (T) entry.getValue();
278 @SuppressWarnings("unchecked")
280 public <T> T query(NodeContext context, PrimitiveQueryKey<T> key) throws NoQueryProcessorException {
282 assert(!ge.isDisposed());
285 queryDebug("Primitive Query[" + ge.getCache().hashCode() + "] " + key + " " + toString(context, key) + " - " + parentKey + " " + toString(parentContext, key) + " " + Thread.currentThread().getName());
289 assert(!(context == parentContext && key == parentKey));
291 // Primitive queries must be leaf queries!
292 assert(!(parentKey instanceof PrimitiveQueryKey));
294 assert(context != null);
299 IGECache cache = ge.getCache();
301 synchronized(ge.getPropagateLock()) {
302 IGECacheEntry entry = cache.getEntry(context, key);
303 if(DEBUG) queryDebug(" CACHED PRIMITIVE RESULT[" + cache.hashCode() + "]: " + ((entry != null) ? (entry.hashCode() + "|" + System.identityHashCode(entry)) : 0));
305 entry = cache.put(context, key, null);
306 PrimitiveQueryProcessor<T> processor = ge.getPrimitiveProcessor(key.processorIdenfitier());
307 if(processor == null) {
308 throw new NoQueryProcessorException(key);
310 // queryDebug("PERFORMING PRIMITIVE QUERY...");
311 T value = processor.query(new GENodeQueryManager(this, context, key, null), context, key);
312 // queryDebug("PRIMITIVE RESULT: " + value);
313 entry.setValue(value);
316 if(treeReference != null) {
317 UIElementReference cachedTreeReference = treeReference;
319 Set<UIElementReference> oldRefs = cache.getTreeReference(context, key);
320 if (oldRefs != null) {
321 if (cachedTreeReference.isDisposed()) {
322 oldRefs.remove(cachedTreeReference);
324 cache.putTreeReference(context, key, cachedTreeReference);
327 cache.putTreeReference(context, key, cachedTreeReference);
331 if(parentContext != null) {
332 assert(parentKey != null);
333 IGECacheEntry parentEntry = cache.getEntry(parentContext, parentKey);
334 if(parentEntry != null) {
335 entry.addDependency(parentEntry);
339 result = (T) entry.getValue();
347 public <T> DataSource<T> tryGetDataSource(Class<T> clazz) {
348 return ge.getDataSource(clazz);
352 public <T> DataSource<T> getDataSource(Class<T> clazz) {
353 DataSource<T> dsp = ge.getDataSource(clazz);
355 throw new NoDataSourceException(clazz);
360 // public <T> void scheduleClear(final INodeContext context, final PrimitiveQueryKey<T> key) {
361 // ge.scheduler.execute(new Runnable() {
363 // public void run() {
364 // synchronized(ge.propagate) {
365 // clearResult(context, key, 0);
373 // public <T> void create(final INodeContext context, final PrimitiveQueryKey<T> key, final T newResult) {
374 // ge.cache.put(context, key, newResult);
378 public <T> void scheduleReplace(final NodeContext context, final PrimitiveQueryKey<T> key, final T newResult) {
380 if(DEBUG) queryDebug("scheduleReplace[" + ge.getCache().hashCode() + "] context=" + context + " key=" + key);
382 IGraphExplorerContext ge = this.ge;
386 class PropagateRunner implements Runnable {
390 IGraphExplorerContext ge = GENodeQueryManager.this.ge;
396 List<Runnable> todo = null;
398 synchronized(ge.getPropagateListLock()) {
400 ge.setPropagating(true);
402 List<Runnable> scheduleList = ge.getScheduleList();
403 Deque<Integer> activity = ge.getActivity();
405 activity.addFirst(scheduleList.size());
409 for(int i : activity) {
412 ge.setActivityInt(activityInt);
414 if(activityInt < 100) {
416 //System.out.println("Scheduling propagate after 10ms.");
417 } else if (activityInt < 1000) {
419 //System.out.println("Scheduling propagate after 500ms.");
422 //System.out.println("Scheduling propagate after 3000ms.");
425 todo = ge.getScheduleList();
426 ge.setScheduleList(new ArrayList<Runnable>());
433 } catch (InterruptedException e) {
439 synchronized(ge.getPropagateLock()) {
441 for(Runnable r : todo) r.run();
447 synchronized(ge.getPropagateListLock()) {
449 ge.setPropagating(false);
451 if(!ge.getScheduleList().isEmpty())
452 ge.scheduleQueryUpdate(new PropagateRunner());
460 synchronized(ge.getPropagateListLock()) {
461 // System.out.println("Schedule Replace: " + key + " - " + context);
462 // new Exception().printStackTrace();
463 List<Runnable> scheduleList = ge.getScheduleList();
464 scheduleList.add(new Runnable() {
467 replaceResult(context, key, newResult, 0);
471 if(ge.isPropagating()) return;
473 ge.scheduleQueryUpdate(new PropagateRunner());
478 public boolean isDisposed() {
481 if (ge.isDisposed()) {
489 public boolean isShown(NodeContext context) {
490 IGraphExplorerContext ge = this.ge;
493 return ge.getCache().isShown(context);
497 public void incRef(NodeContext context) {
498 IGraphExplorerContext ge = this.ge;
501 ge.getCache().incRef(context);
505 public void decRef(NodeContext context) {
506 IGraphExplorerContext ge = this.ge;
509 ge.getCache().decRef(context);