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