]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.browsing.ui.common/src/org/simantics/browsing/ui/common/internal/GENodeQueryManager.java
d6bba94bff8ad194f517c7adca8ce68cd3033dd4
[simantics/platform.git] / bundles / org.simantics.browsing.ui.common / src / org / simantics / browsing / ui / common / internal / GENodeQueryManager.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.browsing.ui.common.internal;
13
14 import java.util.ArrayList;
15 import java.util.Deque;
16 import java.util.List;
17 import java.util.Set;
18
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;
32
33 public class GENodeQueryManager implements NodeQueryManager, PrimitiveQueryUpdater {
34
35     private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(GENodeQueryManager.class);
36     private static final boolean DEBUG = false;
37
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>();
44
45     void indent() {
46         ge.queryIndent(1);
47     }
48
49     void unindent() {
50         ge.queryIndent(-1);
51     }
52
53     void queryDebug(String s) {
54         for (int i = 0; i < ge.queryIndent(); ++i)
55             LOGGER.info("   ");
56         LOGGER.info(s);
57     }
58
59     String toString(UIElementReference ref) {
60         return ref == null ? "" : ref.toString();
61     }
62
63     String toString(Set<UIElementReference> refs) {
64         if (refs == null || refs.isEmpty())
65             return "{}";
66         StringBuilder b = new StringBuilder();
67         for (UIElementReference ref : refs) {
68             b.append(toString(ref));
69         }
70         return b.toString();
71     }
72
73     String toString(NodeContext ctx, CacheKey<?> key) {
74         if (ctx == null)
75             return "null";
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);
80     }
81
82
83     public GENodeQueryManager(GENodeQueryManager parent, NodeContext parentContext, CacheKey<?> parentKey, UIElementReference treeReference) {
84         this.ge = parent.ge;
85         this.parentContext = parentContext;
86         this.parentKey = parentKey;
87         this.treeReference = treeReference;
88         parent.children.add(this);
89     }
90     public GENodeQueryManager(IGraphExplorerContext ge, NodeContext parentContext, CacheKey<?> parentKey, UIElementReference treeReference) {
91         this.ge = ge;
92         this.parentContext = parentContext;
93         this.parentKey = parentKey;
94         this.treeReference = treeReference;
95     }
96
97     @Override
98     public Object getExplorerContext() {
99         return ge;
100     }
101     
102     public void dispose() {
103         if (disposed)
104                 return;
105         disposed = true;
106         if (ge != null && parentContext != null && parentKey != null) {
107                 ge.getCache().remove(parentContext, parentKey);
108         }
109         ge = null;
110         parentContext = null;
111         parentKey = null;
112         treeReference = null;
113         for (GENodeQueryManager m : children)
114                 m.dispose();
115         children.clear();
116         children = null;
117     }
118
119 //    @Override
120 //    public String toString() {
121 //        return "GENodeQueryManager[parentKey=" + parentKey + ", parentContext=" + "]";
122 //    }
123 //
124 //    @Override
125 //    public CacheKey getParentKey() {
126 //        return parentKey;
127 //    }
128 //
129 //    @Override
130 //    public INodeContext getParentContext() {
131 //        return parentContext;
132 //    }
133
134 //    static int koss = 0;
135     
136 //    @Override
137     public <T> void replaceResult(NodeContext context, PrimitiveQueryKey<T> key, T newResult, int indent) {
138
139         IGraphExplorerContext ge = this.ge;
140         if (isDisposed())
141             return;
142         
143         if(DEBUG) {
144             queryDebug("replaceResult[" + ge.getCache().hashCode() + "] " + key + " -> " + newResult);
145             indent();
146         }
147         
148 //        if((koss++ % 5000) == 0) {
149 //            System.out.println("R" + koss);
150 //        }
151         
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);
157
158             Set<UIElementReference> refs = cache.removeTreeReference(context, key);
159
160             if (refs != null) {
161                 //queryDebug("(replaceResult) found tree references " + toString(refs));
162                 for (UIElementReference ref : refs)
163                     ge.update(ref);
164             }
165         } else {
166             // TODO: explain why this check is here or remove it!
167
168             // Consistency checking, no TreeReference should ever exist in this case!
169             Set<UIElementReference> ref = cache.getTreeReference(context, key);
170             assert ref == null;
171         }
172     }
173
174 //    @Override
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);
178 //        }
179         if(DEBUG) queryDebug("clearResult[" + ge.getCache().hashCode() + "] " + key + " " + context);
180
181         IGraphExplorerContext ge = this.ge;
182         if (isDisposed())
183             return;
184
185         IGECache cache = ge.getCache();
186         IGECacheEntry entry = cache.getEntry(context, key);
187         if (entry != null) {
188             cache.remove(context, key);
189             propagate(context, key, entry, indent);
190         }
191
192         Set<UIElementReference> refs = cache.removeTreeReference(context, key);
193         if (refs != null) {
194             //queryDebug("(clearResult) found tree reference " + toString(refs));
195             for (UIElementReference ref : refs)
196                 ge.update(ref);
197         }
198     }
199
200     public <T> void propagate(NodeContext context, CacheKey<T> key, IGECacheEntry entry, int indent) {
201
202         if (isDisposed())
203             return;
204
205         if(DEBUG) queryDebug("propagate[" + ge.getCache().hashCode() + "] " + key + " - " + context);
206
207         assert entry != null;
208
209         for(IGECacheEntry dependency : entry.getDependencies()) {
210             clearResult(dependency.getContext(), dependency.getKey(), indent + 3);
211         }
212
213         entry.reset();
214     }
215
216     @SuppressWarnings("unchecked")
217     @Override
218     public <T> T query(NodeContext context, QueryKey<T> key) throws NoQueryProcessorException {
219
220         assert(!ge.isDisposed());
221
222         if(DEBUG) {
223             queryDebug("Query[" + ge.getCache().hashCode() + "] " + key + " " + toString(context, key) + " - " + parentKey + " " + toString(parentContext, parentKey));
224             indent();
225         }
226
227         assert(!(context == parentContext && key == parentKey));
228
229         assert(context != null);
230         assert(key != null);
231
232         T result = null;
233         IGECache cache = ge.getCache();
234
235         synchronized(ge.getPropagateLock()) {
236
237             IGECacheEntry entry = cache.getEntry(context, key);
238             //queryDebug("  CACHED RESULT: " + entry);
239             if(entry == null) {
240                 entry = cache.put(context, key, null);
241                 NodeQueryProcessor<T> processor = ge.getProcessor(key);
242                 if(processor == null) {
243                     throw new NoQueryProcessorException(key);
244                 }
245 //                queryDebug("PERFORMING QUERY...");
246                 T value = processor.query(new GENodeQueryManager(this, context, key, null), context);
247 //                queryDebug("RESULT: " + value);
248                 entry.setValue(value);
249             }
250
251             if(treeReference != null) {
252                 UIElementReference cachedTreeReference = treeReference;
253
254                 Set<UIElementReference> oldRefs = cache.getTreeReference(context, key);
255                 if (oldRefs != null) {
256                     if (cachedTreeReference.isDisposed()) {
257                         oldRefs.remove(cachedTreeReference);
258                     } else {
259                         cache.putTreeReference(context, key, cachedTreeReference);
260                     }
261                 } else {
262                     cache.putTreeReference(context, key, cachedTreeReference);
263                 }
264             }
265
266             if(parentContext != null) {
267                 assert(parentKey != null);
268                 IGECacheEntry parentEntry = cache.getEntry(parentContext, parentKey);
269                 if(parentEntry != null)
270                     entry.addDependency(parentEntry);
271             }
272
273             result = (T) entry.getValue();
274         }
275
276         unindent();
277         return result;
278     }
279
280     @SuppressWarnings("unchecked")
281     @Override
282     public <T> T query(NodeContext context, PrimitiveQueryKey<T> key) throws NoQueryProcessorException {
283
284         assert(!ge.isDisposed());
285
286         if(DEBUG) {
287             queryDebug("Primitive Query[" + ge.getCache().hashCode() + "] " + key + " " + toString(context, key) + " - " + parentKey + " " + toString(parentContext, key) + " " + Thread.currentThread().getName());
288             indent();
289         }
290
291         assert(!(context == parentContext && key == parentKey));
292
293         // Primitive queries must be leaf queries!
294         assert(!(parentKey instanceof PrimitiveQueryKey));
295
296         assert(context != null);
297         assert(key != null);
298
299         T result = null;
300
301         IGECache cache = ge.getCache();
302
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));
306             if(entry == null) {
307                 entry = cache.put(context, key, null);
308                 PrimitiveQueryProcessor<T> processor = ge.getPrimitiveProcessor(key.processorIdenfitier());
309                 if(processor == null) {
310                     throw new NoQueryProcessorException(key);
311                 }
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);
316             }
317
318             if(treeReference != null) {
319                 UIElementReference cachedTreeReference = treeReference;
320
321                 Set<UIElementReference> oldRefs = cache.getTreeReference(context, key);
322                 if (oldRefs != null) {
323                     if (cachedTreeReference.isDisposed()) {
324                         oldRefs.remove(cachedTreeReference);
325                     } else {
326                         cache.putTreeReference(context, key, cachedTreeReference);
327                     }
328                 } else {
329                     cache.putTreeReference(context, key, cachedTreeReference);
330                 }
331             }
332
333             if(parentContext != null) {
334                 assert(parentKey != null);
335                 IGECacheEntry parentEntry = cache.getEntry(parentContext, parentKey);
336                 if(parentEntry != null) {
337                     entry.addDependency(parentEntry);
338                 }
339             }
340
341             result = (T) entry.getValue();
342         }
343
344         unindent();
345         return result;
346     }
347
348     @Override
349     public <T> DataSource<T> tryGetDataSource(Class<T> clazz) {
350         return ge.getDataSource(clazz);
351     }
352
353     @Override
354     public <T> DataSource<T> getDataSource(Class<T> clazz) {
355         DataSource<T> dsp = ge.getDataSource(clazz);
356         if (dsp == null)
357             throw new NoDataSourceException(clazz);
358         return dsp;
359     }
360
361 //    @Override
362 //    public <T> void scheduleClear(final INodeContext context, final PrimitiveQueryKey<T> key) {
363 //        ge.scheduler.execute(new Runnable() {
364 //            @Override
365 //            public void run() {
366 //                synchronized(ge.propagate) {
367 //                    clearResult(context, key, 0);
368 //                }
369 //            }
370 //        });
371 //    }
372
373
374 //    @Override
375 //    public <T> void create(final INodeContext context, final PrimitiveQueryKey<T> key, final T newResult) {
376 //        ge.cache.put(context, key, newResult);
377 //    }
378
379     @Override
380     public <T> void scheduleReplace(final NodeContext context, final PrimitiveQueryKey<T> key, final T newResult) {
381
382         if(DEBUG) queryDebug("scheduleReplace[" + ge.getCache().hashCode() + "] context=" + context + " key=" + key);
383
384         IGraphExplorerContext ge = this.ge;
385         if (isDisposed())
386             return;
387
388         class PropagateRunner implements Runnable {
389
390             @Override
391             public void run() {
392                 IGraphExplorerContext ge = GENodeQueryManager.this.ge;
393                 if (isDisposed())
394                     return;
395
396                 int delay = 0;
397
398                 List<Runnable> todo = null;
399                 
400                 synchronized(ge.getPropagateListLock()) {
401
402                     ge.setPropagating(true);
403
404                     List<Runnable> scheduleList = ge.getScheduleList();
405                     Deque<Integer> activity = ge.getActivity();
406
407                     activity.addFirst(scheduleList.size());
408                     activity.pollLast();
409
410                     int activityInt = 0;
411                     for(int i : activity) {
412                         activityInt += i;
413                     }
414                     ge.setActivityInt(activityInt);
415
416                     if(activityInt < 100) {
417                         delay = 10;
418                         //System.out.println("Scheduling propagate after 10ms.");
419                     } else if (activityInt < 1000) {
420                         delay = 500;
421                         //System.out.println("Scheduling propagate after 500ms.");
422                     } else {
423                         delay = 3000;
424                         //System.out.println("Scheduling propagate after 3000ms.");
425                     }
426                     
427                     todo = ge.getScheduleList();
428                     ge.setScheduleList(new ArrayList<Runnable>());
429
430                 }
431
432                 try {
433                     if(delay > 0)
434                         Thread.sleep(delay);
435                 } catch (InterruptedException e) {
436                     e.printStackTrace();
437                 }
438                 if (isDisposed())
439                         return;
440                 
441                 synchronized(ge.getPropagateLock()) {
442
443                     for(Runnable r : todo) r.run();
444
445                 }
446                 if (isDisposed())
447                         return;
448                 
449                 synchronized(ge.getPropagateListLock()) {
450
451                     ge.setPropagating(false);
452
453                     if(!ge.getScheduleList().isEmpty())
454                         ge.scheduleQueryUpdate(new PropagateRunner());
455
456                 }
457
458             }
459
460         }
461
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() {
467                 @Override
468                 public void run() {
469                     replaceResult(context, key, newResult, 0);
470                 }
471             });
472
473             if(ge.isPropagating()) return;
474
475             ge.scheduleQueryUpdate(new PropagateRunner());
476         }
477     }
478
479     @Override
480     public boolean isDisposed() {
481         if (disposed)
482                 return true;
483         if (ge.isDisposed()) {
484                 dispose();
485                 return true;
486         }
487         return false;
488     }
489     
490     @Override
491     public boolean isShown(NodeContext context) {
492         IGraphExplorerContext ge = this.ge;
493         if (isDisposed())
494                 return false;
495         return ge.getCache().isShown(context);
496     }
497
498     @Override
499     public void incRef(NodeContext context) {
500         IGraphExplorerContext ge = this.ge;
501         if (isDisposed())
502                 return;
503         ge.getCache().incRef(context);
504     }
505     
506     @Override
507     public void decRef(NodeContext context) {
508         IGraphExplorerContext ge = this.ge;
509         if (isDisposed())
510                 return;
511         ge.getCache().decRef(context);
512     }
513     
514     public void execFromQuery(Runnable runnable) {
515         ge.execFromQuery(runnable);
516     }
517     
518 }