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