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