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