]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntryBase.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.db.impl / src / org / simantics / db / impl / query / CacheEntryBase.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.db.impl.query;\r
13 \r
14 import java.util.ArrayList;\r
15 import java.util.Iterator;\r
16 \r
17 import org.simantics.db.common.utils.Logger;\r
18 import org.simantics.db.exception.DatabaseException;\r
19 import org.simantics.db.impl.DebugPolicy;\r
20 import org.simantics.db.impl.graph.ReadGraphImpl;\r
21 import org.simantics.db.impl.procedure.InternalProcedure;\r
22 \r
23 abstract public class CacheEntryBase extends CacheEntry {\r
24 \r
25         // Default level is something that is not quite a prospect but still allows for ordering within CacheCollectionResult \r
26         public static final short UNDEFINED_LEVEL = 5;\r
27         \r
28         public short level = UNDEFINED_LEVEL;\r
29         public short age = 0;\r
30         public int GCStatus = 0;\r
31         \r
32     final public static CacheEntryBase[] NONE = new CacheEntryBase[0];\r
33 \r
34         static private Object NO_RESULT = new Object();\r
35         static protected Object INVALID_RESULT = new Object();\r
36         \r
37         // Just created\r
38     static protected Object FRESH = new Object() { public String toString() { return "CREATED"; }};\r
39     // Result is computed - no exception\r
40     static protected Object READY = new Object() { public String toString() { return "READY"; }};\r
41     // Computation is under way\r
42     static protected Object PENDING = new Object() { public String toString() { return "PENDING"; }};\r
43     // Entry is discarded and is waiting for garbage collect\r
44     static protected Object DISCARDED = new Object() { public String toString() { return "DISCARDED"; }};\r
45     // The result has been invalidated\r
46     static protected Object REFUTED = new Object() { public String toString() { return "REFUTED"; }};\r
47     // The computation has excepted - the exception is in the result\r
48     static protected Object EXCEPTED = new Object() { public String toString() { return "EXCEPTED"; }};\r
49 \r
50     // This indicates the status of the entry\r
51     public Object statusOrException = FRESH;\r
52     \r
53     private CacheEntry p1 = null;\r
54     private Object p2OrParents = null;\r
55     \r
56     private int hash = 0;\r
57     \r
58     @Override\r
59     final public int hashCode() {\r
60         if(hash == 0) hash = makeHash();\r
61         return hash;\r
62     }\r
63     \r
64     abstract int makeHash();\r
65     \r
66     // This can be tested to see if the result is finished\r
67     private Object result = NO_RESULT;\r
68     \r
69     final public boolean isFresh() {\r
70         return FRESH == statusOrException;\r
71     }\r
72 \r
73     public void setReady() {\r
74         statusOrException = READY;\r
75     }\r
76 \r
77     @Deprecated\r
78     final public boolean isReady() {\r
79         return READY == statusOrException || EXCEPTED == statusOrException;\r
80     }\r
81     \r
82     @Override\r
83     public void discard() {\r
84         if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: discarded " + this);\r
85         statusOrException = DISCARDED;\r
86     }\r
87     \r
88     @Override\r
89     final public boolean isDiscarded() {\r
90         return DISCARDED == statusOrException;\r
91     }\r
92     \r
93     @Override\r
94     final public void refute() {\r
95         if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: refuted " + this);\r
96         statusOrException = REFUTED;\r
97     }\r
98     \r
99     @Override\r
100     final public boolean isRefuted() {\r
101         return REFUTED == statusOrException;\r
102     }\r
103 \r
104     @Override\r
105     final public void except(Throwable t) {\r
106         if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: excepted " + this);\r
107         if(statusOrException != DISCARDED) {\r
108                 statusOrException = EXCEPTED;\r
109                 result = t;\r
110         } else {\r
111                 Logger.defaultLogError("Cache entry got excepted status after being discarded: " + getClass().getSimpleName(), t);\r
112                 result = t;\r
113         }\r
114     }\r
115     \r
116     final public void checkAndThrow() throws DatabaseException {\r
117         if(isExcepted()) {\r
118             Throwable throwable = (Throwable)result;\r
119             if(throwable instanceof DatabaseException) throw (DatabaseException)throwable;\r
120             else throw new DatabaseException(throwable);\r
121         }\r
122     }\r
123     \r
124     @Override\r
125     final public boolean isExcepted() {\r
126         return EXCEPTED == statusOrException;\r
127     }\r
128 \r
129     @Override\r
130     final public void setPending() {\r
131         statusOrException = PENDING;\r
132     }\r
133     \r
134     @Override\r
135     final public boolean isPending() {\r
136         return PENDING == statusOrException;\r
137     }\r
138     \r
139     final public boolean assertPending() {\r
140         boolean result = isPending();\r
141         if(!result) {\r
142                 System.err.println("Assertion failed, expected pending, got " + statusOrException);\r
143         }\r
144         return result;\r
145     }\r
146 \r
147     final public boolean assertNotPending() {\r
148         boolean result = !isPending();\r
149         if(!result) {\r
150                 new Exception(this +  ": Assertion failed, expected not pending, got " + statusOrException).printStackTrace();\r
151         }\r
152         return result;\r
153     }\r
154 \r
155     final public boolean assertNotDiscarded() {\r
156         boolean result = !isDiscarded();\r
157         if(!result) {\r
158                 new Exception(this +  ": Assertion failed, expected not discarded, got " + statusOrException).printStackTrace();\r
159         }\r
160         return result;\r
161     }\r
162 \r
163     @Override\r
164     public void setResult(Object result) {\r
165         this.result = result;\r
166     }\r
167     \r
168     @Override\r
169     final public <T> T getResult() {\r
170         assert(statusOrException != DISCARDED);\r
171         return (T)result;\r
172     }\r
173     \r
174     @Override\r
175     public void clearResult(QuerySupport support) {\r
176         setResult(NO_RESULT);\r
177     }\r
178     \r
179     @Override\r
180     final public void addParent(CacheEntry entry) {\r
181          \r
182         assert(entry != null);\r
183         \r
184         if(p1 == entry) {\r
185                 return;\r
186         }\r
187         if(p2OrParents == entry) {\r
188                 return;\r
189         }\r
190         if(p1 == null) {\r
191                 p1 = entry;\r
192         } else if(p2OrParents == null) {\r
193                 p2OrParents = entry;\r
194         } else if(p2OrParents instanceof QueryIdentityHashSet) {\r
195             ((QueryIdentityHashSet)p2OrParents).add(entry);\r
196             ((QueryIdentityHashSet)p2OrParents).purge();\r
197         } else {\r
198             CacheEntry tmp = (CacheEntry)p2OrParents;\r
199             p2OrParents = new QueryIdentityHashSet(2);\r
200             ((QueryIdentityHashSet)p2OrParents).add(tmp);\r
201             ((QueryIdentityHashSet)p2OrParents).add(entry);\r
202         }\r
203         \r
204     }\r
205 \r
206     @Override\r
207     CacheEntry pruneFirstParents() {\r
208 \r
209         if(p1 == null) {\r
210                 // No parents\r
211                 return null;\r
212         }\r
213         \r
214         if(!p1.isDiscarded()) {\r
215                 \r
216                 // First parent is still active\r
217                 return p1;\r
218                 \r
219         } else {\r
220                 \r
221                 // Clear p1\r
222                 p1 = null;\r
223                 \r
224                 // First parent is discarded => look for more parents\r
225                 if(p2OrParents instanceof QueryIdentityHashSet) {\r
226 \r
227                         QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;\r
228                         CacheEntry entry = set.removeDiscarded();\r
229                         if(entry == null) p2OrParents = null;\r
230                         p1 = entry;\r
231                         return p1;\r
232 \r
233                 } else if(p2OrParents instanceof CacheEntry) {\r
234                         \r
235                         CacheEntry entry = (CacheEntry)p2OrParents;\r
236                         if(entry.isDiscarded()) {\r
237                                 // Second entry is also discarded => all empty\r
238                                 p2OrParents = null;\r
239                                 return null;\r
240                         } else {\r
241                                 p1 = entry;\r
242                                 p2OrParents = null;\r
243                                 return p1;\r
244                         }\r
245                         \r
246                 } else {\r
247                 \r
248                         // Nothing left\r
249                         return null;\r
250                         \r
251                 }\r
252                 \r
253         }\r
254         \r
255     }\r
256     \r
257     @Override\r
258     final public void removeParent(CacheEntry entry) {\r
259        \r
260         if(p1 == null) {\r
261             if(p2OrParents != null) throw new Error("CacheEntryBase.removeParent: corrupted parents (p1 == null, while p2OrParents != null).");\r
262             else throw new Error("CacheEntryBase.removeParent: no parents.");\r
263         }\r
264         if(p1 == entry) {\r
265             if(p2OrParents == null) {\r
266                 p1 = null;\r
267             } else if(p2OrParents instanceof QueryIdentityHashSet) {\r
268                 QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;\r
269                 int size = set.size();\r
270                 if(size == 0) {\r
271                     p1 = null;\r
272                     p2OrParents = null;\r
273                 } else if (size == 1) {\r
274                     CacheEntry next = set.iterator().next();\r
275                     p1 = next;\r
276                     set = null;\r
277                 } else if(set.size() == 2) {\r
278                     Iterator<CacheEntry> iterator = set.iterator();\r
279                     p1 = iterator.next();\r
280                     p2OrParents = iterator.next();\r
281                 } else {\r
282                     p1 = set.iterator().next();\r
283                     set.remove(p1);\r
284                 }\r
285             } else {\r
286                 p1 = (CacheEntry)p2OrParents;\r
287                 p2OrParents = null;\r
288             }\r
289             \r
290         } else if(p2OrParents.getClass() == QueryIdentityHashSet.class) {\r
291             \r
292             QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;\r
293             boolean success = set.remove(entry);\r
294             if(!success) {\r
295                 throw new Error("CacheEntryBase.removeParent: parent was not found.");\r
296             }\r
297             assert(set.size() >= 1);\r
298             if(set.size() == 1) {\r
299                 p2OrParents = set.iterator().next();\r
300             }\r
301             \r
302         } else {\r
303             if(p2OrParents == entry) {\r
304                 p2OrParents = null;\r
305             } else {\r
306                 throw new Error("CacheEntryBase.removeParent: had 2 parents but neither was removed.");\r
307             }\r
308         }\r
309     }\r
310 \r
311     @Override\r
312     final public boolean hasParents() {\r
313         assert(statusOrException != DISCARDED);\r
314         return p1 != null;\r
315     }\r
316     \r
317     @Override\r
318         final public Iterable<CacheEntry> getParents(QueryProcessor processor) {\r
319 \r
320                 ArrayList<CacheEntry> result = new ArrayList<CacheEntry>();\r
321                 if(p1 != null) result.add(p1);\r
322                 if(p2OrParents != null) {\r
323                 if(p2OrParents instanceof QueryIdentityHashSet) {\r
324                         for(CacheEntry entry : (QueryIdentityHashSet)p2OrParents) {\r
325                                 result.add(entry);\r
326                         }\r
327                 } else {\r
328                         result.add((CacheEntry)p2OrParents);\r
329                 }\r
330                 }\r
331                 fillImpliedParents(processor, result);\r
332                 return result;\r
333                 \r
334         }\r
335     \r
336     @Override\r
337     CacheEntry getFirstParent(QueryProcessor processor) {\r
338         return p1;\r
339     }\r
340     \r
341     @Override\r
342     boolean moreThanOneParent(QueryProcessor processor) {\r
343         return p2OrParents != null;\r
344     }\r
345     \r
346     @Override\r
347     int parentCount(QueryProcessor processor) {\r
348         if(p2OrParents != null) {\r
349                 if(p2OrParents instanceof QueryIdentityHashSet) {\r
350                         return ((QueryIdentityHashSet)p2OrParents).size()+1;\r
351                 } else {\r
352                         return 2;\r
353                 }\r
354         } else {\r
355                 return p1 != null ? 1 : 0;\r
356         }\r
357         \r
358     }\r
359     \r
360     protected void fillImpliedParents(QueryProcessor processor, ArrayList<CacheEntry> result) {\r
361         \r
362     }\r
363     \r
364     protected String internalError() {\r
365         return toString() + " " + statusOrException + " " + result;\r
366     }\r
367 \r
368     \r
369     protected boolean handleException(ReadGraphImpl graph, IntProcedure procedure) {\r
370         if(isExcepted()) {\r
371                 procedure.exception(graph, (Throwable)getResult());\r
372                 return true;\r
373         } else {\r
374                 return false;\r
375         }\r
376     }\r
377     \r
378     protected boolean handleException(ReadGraphImpl graph, TripleIntProcedure procedure) {\r
379         if(isExcepted()) {\r
380                 procedure.exception(graph, (Throwable)getResult());\r
381                 return true;\r
382         } else {\r
383                 return false;\r
384         }\r
385     }\r
386 \r
387     protected <T> boolean handleException(ReadGraphImpl graph, InternalProcedure<T> procedure) {\r
388         if(isExcepted()) {\r
389                 procedure.exception(graph, (Throwable)getResult());\r
390                 return true;\r
391         } else {\r
392                 return false;\r
393         }\r
394     }\r
395     \r
396     @Override\r
397     boolean isImmutable(ReadGraphImpl graph) throws DatabaseException {\r
398         return false;\r
399     }\r
400     \r
401     @Override\r
402     boolean shouldBeCollected() {\r
403         return true;\r
404     }\r
405     \r
406     @Override\r
407     short getLevel() {\r
408         return level;\r
409     }\r
410     \r
411     @Override\r
412     short setLevel(short level) {\r
413         short existing = this.level;\r
414         this.level = level;\r
415         return existing;\r
416     }\r
417     \r
418     @Override\r
419     void prepareRecompute(QuerySupport querySupport) {\r
420                 setPending();\r
421                 clearResult(querySupport);\r
422     }\r
423     \r
424     /*\r
425      * \r
426      * \r
427      */\r
428     @Override\r
429     int getGCStatus() {\r
430         return GCStatus;\r
431     }\r
432     \r
433     @Override\r
434     int setGCStatus(int status) {\r
435         GCStatus = status;\r
436         return GCStatus;\r
437     }\r
438     \r
439     @Override\r
440     void setGCStatusFlag(int flag, boolean value) {\r
441         if(value) {\r
442                 GCStatus |= flag;\r
443         } else {\r
444                 GCStatus &= ~flag;\r
445         }\r
446     }\r
447     \r
448     @Override\r
449     public Object getOriginalRequest() {\r
450         // This is the original request for all built-in queries\r
451         return getQuery();\r
452     }\r
453     \r
454 }\r