]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntryBase.java
7c87b50e3acfa0e4630f3eb93ba8859c1eb13c90
[simantics/platform.git] / bundles / org.simantics.db.impl / src / org / simantics / db / impl / query / CacheEntryBase.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2018 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 public abstract class CacheEntryBase<Procedure> extends CacheEntry<Procedure> {
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 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 REQUIRES_COMPUTATION = 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 = REQUIRES_COMPUTATION;
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     Object result = NO_RESULT;
71     
72     final public boolean isFresh() {
73         return REQUIRES_COMPUTATION == 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 = REQUIRES_COMPUTATION;
100     }
101     
102     @Override
103     final public boolean isRefuted() {
104         return REQUIRES_COMPUTATION == statusOrException;
105     }
106
107     @Override
108     public void except(Throwable throwable) {
109         if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: excepted " + this);
110         if(statusOrException != DISCARDED) {
111                 statusOrException = EXCEPTED;
112                 result = throwable;
113         } else {
114                 LOGGER.warn("Cache entry got excepted status after being discarded: " + getClass().getSimpleName(), throwable);
115                 result = throwable;
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     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 requiresComputation() {
143         return REQUIRES_COMPUTATION == statusOrException;
144     }
145     
146     final public boolean assertPending() {
147         boolean result = isPending();
148         if(!result) {
149                 LOGGER.warn("Assertion failed, expected pending, got " + statusOrException);
150         }
151         return result;
152     }
153
154     final public boolean assertNotPending() {
155         boolean result = !isPending();
156         if(!result) {
157                 new Exception(this +  ": Assertion failed, expected not pending, got " + statusOrException).printStackTrace();
158         }
159         return result;
160     }
161
162     final public boolean assertNotDiscarded() {
163         boolean result = !isDiscarded();
164         if(!result) {
165                 new Exception(this +  ": Assertion failed, expected not discarded, got " + statusOrException).printStackTrace();
166         }
167         return result;
168     }
169
170     @Override
171     public void setResult(Object result) {
172         this.result = result;
173     }
174     
175     @SuppressWarnings("unchecked")
176     @Override
177     final public <T> T getResult() {
178         assert(statusOrException != DISCARDED);
179         return (T)result;
180     }
181     
182     @Override
183     public void clearResult(QuerySupport support) {
184         setResult(NO_RESULT);
185     }
186     
187     @Override
188     final public void addParent(CacheEntry entry) {
189          
190         assert(entry != null);
191         
192         if(p1 == entry) {
193                 return;
194         }
195         if(p2OrParents == entry) {
196                 return;
197         }
198         if(p1 == null) {
199                 p1 = entry;
200         } else if(p2OrParents == null) {
201                 p2OrParents = entry;
202         } else if(p2OrParents instanceof QueryIdentityHashSet) {
203             ((QueryIdentityHashSet)p2OrParents).add(entry);
204             ((QueryIdentityHashSet)p2OrParents).purge();
205         } else {
206             CacheEntry tmp = (CacheEntry)p2OrParents;
207             p2OrParents = new QueryIdentityHashSet(2);
208             ((QueryIdentityHashSet)p2OrParents).add(tmp);
209             ((QueryIdentityHashSet)p2OrParents).add(entry);
210         }
211         
212     }
213
214     @Override
215     CacheEntry pruneFirstParents() {
216
217         if(p1 == null) {
218                 // No parents
219                 return null;
220         }
221         
222         if(!p1.isDiscarded()) {
223                 
224                 // First parent is still active
225                 return p1;
226                 
227         } else {
228                 
229                 // Clear p1
230                 p1 = null;
231                 
232                 // First parent is discarded => look for more parents
233                 if(p2OrParents instanceof QueryIdentityHashSet) {
234
235                         QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
236                         CacheEntry entry = set.removeDiscarded();
237                         if(entry == null) p2OrParents = null;
238                         p1 = entry;
239                         return p1;
240
241                 } else if(p2OrParents instanceof CacheEntry) {
242                         
243                         CacheEntry entry = (CacheEntry)p2OrParents;
244                         if(entry.isDiscarded()) {
245                                 // Second entry is also discarded => all empty
246                                 p2OrParents = null;
247                                 return null;
248                         } else {
249                                 p1 = entry;
250                                 p2OrParents = null;
251                                 return p1;
252                         }
253                         
254                 } else {
255                 
256                         // Nothing left
257                         return null;
258                         
259                 }
260                 
261         }
262         
263     }
264     
265     @Override
266     final public void removeParent(CacheEntry entry) {
267        
268         if(p1 == null) {
269             if(p2OrParents != null) throw new Error("CacheEntryBase.removeParent: corrupted parents (p1 == null, while p2OrParents != null).");
270             else throw new Error("CacheEntryBase.removeParent: no parents.");
271         }
272         if(p1 == entry) {
273             if(p2OrParents == null) {
274                 p1 = null;
275             } else if(p2OrParents instanceof QueryIdentityHashSet) {
276                 QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
277                 int size = set.size();
278                 if(size == 0) {
279                     p1 = null;
280                     p2OrParents = null;
281                 } else if (size == 1) {
282                     CacheEntry next = set.iterator().next();
283                     p1 = next;
284                     set = null;
285                 } else if(set.size() == 2) {
286                     Iterator<CacheEntry> iterator = set.iterator();
287                     p1 = iterator.next();
288                     p2OrParents = iterator.next();
289                 } else {
290                     p1 = set.iterator().next();
291                     set.remove(p1);
292                 }
293             } else {
294                 p1 = (CacheEntry)p2OrParents;
295                 p2OrParents = null;
296             }
297             
298         } else if(p2OrParents.getClass() == QueryIdentityHashSet.class) {
299             
300             QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
301             boolean success = set.remove(entry);
302             if(!success) {
303                 throw new Error("CacheEntryBase.removeParent: parent was not found.");
304             }
305             assert(set.size() >= 1);
306             if(set.size() == 1) {
307                 p2OrParents = set.iterator().next();
308             }
309             
310         } else {
311             if(p2OrParents == entry) {
312                 p2OrParents = null;
313             } else {
314                 throw new Error("CacheEntryBase.removeParent: had 2 parents but neither was removed.");
315             }
316         }
317     }
318
319     @Override
320     final public boolean hasParents() {
321         assert(statusOrException != DISCARDED);
322         return p1 != null;
323     }
324     
325     @Override
326         final public Iterable<CacheEntry<?>> getParents(QueryProcessor processor) {
327
328                 ArrayList<CacheEntry<?>> result = new ArrayList<CacheEntry<?>>();
329                 if(p1 != null) result.add(p1);
330                 if(p2OrParents != null) {
331                 if(p2OrParents instanceof QueryIdentityHashSet) {
332                         for(CacheEntry entry : (QueryIdentityHashSet)p2OrParents) {
333                                 result.add(entry);
334                         }
335                 } else {
336                         result.add((CacheEntry)p2OrParents);
337                 }
338                 }
339                 fillImpliedParents(processor, result);
340                 return result;
341                 
342         }
343     
344     @Override
345     CacheEntry getFirstParent(QueryProcessor processor) {
346         return p1;
347     }
348     
349     @Override
350     boolean moreThanOneParent(QueryProcessor processor) {
351         return p2OrParents != null;
352     }
353     
354     @Override
355     int parentCount(QueryProcessor processor) {
356         if(p2OrParents != null) {
357                 if(p2OrParents instanceof QueryIdentityHashSet) {
358                         return ((QueryIdentityHashSet)p2OrParents).size()+1;
359                 } else {
360                         return 2;
361                 }
362         } else {
363                 return p1 != null ? 1 : 0;
364         }
365         
366     }
367     
368     protected void fillImpliedParents(QueryProcessor processor, ArrayList<CacheEntry<?>> result) {
369     }
370     
371     protected String internalError() {
372         return toString() + " " + statusOrException + " " + result;
373     }
374
375     
376     protected boolean handleException(ReadGraphImpl graph, IntProcedure procedure) throws DatabaseException {
377         if(isExcepted()) {
378                 procedure.exception(graph, (Throwable)getResult());
379                 return true;
380         } else {
381                 return false;
382         }
383     }
384     
385     protected boolean handleException(ReadGraphImpl graph, TripleIntProcedure procedure) throws DatabaseException {
386         if(isExcepted()) {
387                 procedure.exception(graph, (Throwable)getResult());
388                 return true;
389         } else {
390                 return false;
391         }
392     }
393
394     protected <T> boolean handleException(ReadGraphImpl graph, InternalProcedure<T> procedure) throws DatabaseException {
395         if(isExcepted()) {
396                 procedure.exception(graph, (Throwable)getResult());
397                 return true;
398         } else {
399                 return false;
400         }
401     }
402     
403     @Override
404     boolean isImmutable(ReadGraphImpl graph) throws DatabaseException {
405         return false;
406     }
407     
408     @Override
409     boolean shouldBeCollected() {
410         return true;
411     }
412     
413     @Override
414     short getLevel() {
415         return level;
416     }
417     
418     @Override
419     short setLevel(short level) {
420         short existing = this.level;
421         this.level = level;
422         return existing;
423     }
424     
425     @Override
426     void prepareRecompute(QuerySupport querySupport) {
427                 setPending();
428                 clearResult(querySupport);
429     }
430     
431     @Override
432     int getGCStatus() {
433         return GCStatus;
434     }
435     
436     @Override
437     int setGCStatus(int status) {
438         GCStatus = status;
439         return GCStatus;
440     }
441     
442     @Override
443     void setGCStatusFlag(int flag, boolean value) {
444         if(value) {
445                 GCStatus |= flag;
446         } else {
447                 GCStatus &= ~flag;
448         }
449     }
450     
451     @Override
452     public Object getOriginalRequest() {
453         // This is the original request for all built-in queries
454         return getQuery();
455     }
456
457     public CacheEntryBase() {
458     }
459
460 }