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