1 /*******************************************************************************
2 * Copyright (c) 2007, 2018 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.db.impl.query;
14 import java.util.ArrayList;
15 import java.util.Iterator;
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;
24 public abstract class CacheEntryBase<Procedure> extends CacheEntry<Procedure> {
26 private static final Logger LOGGER = LoggerFactory.getLogger(CacheEntryBase.class);
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;
31 public short level = UNDEFINED_LEVEL;
33 public int GCStatus = 0;
35 final public static CacheEntryBase[] NONE = new CacheEntryBase[0];
37 static Object NO_RESULT = new Object();
38 static protected Object INVALID_RESULT = new Object();
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"; }};
53 // This indicates the status of the entry
54 public Object statusOrException = REQUIRES_COMPUTATION;
56 private CacheEntry p1 = null;
57 private Object p2OrParents = null;
62 final public int hashCode() {
63 if(hash == 0) hash = makeHash();
67 abstract int makeHash();
69 // This can be tested to see if the result is finished
70 Object result = NO_RESULT;
72 final public boolean isFresh() {
73 return REQUIRES_COMPUTATION == statusOrException;
76 public void setReady() {
77 statusOrException = READY;
81 final public boolean isReady() {
82 return READY == statusOrException || EXCEPTED == statusOrException;
86 public void discard() {
87 if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: discarded " + this);
88 statusOrException = DISCARDED;
92 final public boolean isDiscarded() {
93 return DISCARDED == statusOrException;
97 final public void refute() {
98 if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: refuted " + this);
99 statusOrException = REQUIRES_COMPUTATION;
103 final public boolean isRefuted() {
104 return REQUIRES_COMPUTATION == statusOrException;
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;
114 LOGGER.warn("Cache entry got excepted status after being discarded: " + getClass().getSimpleName(), throwable);
119 final public void checkAndThrow() throws DatabaseException {
121 Throwable throwable = (Throwable)result;
122 if(throwable instanceof DatabaseException) throw (DatabaseException)throwable;
123 else throw new DatabaseException(throwable);
128 final public boolean isExcepted() {
129 return EXCEPTED == statusOrException;
133 public void setPending() {
134 statusOrException = PENDING;
138 final public boolean isPending() {
139 return PENDING == statusOrException;
142 final public boolean requiresComputation() {
143 return REQUIRES_COMPUTATION == statusOrException;
146 final public boolean assertPending() {
147 boolean result = isPending();
149 LOGGER.warn("Assertion failed, expected pending, got " + statusOrException);
154 final public boolean assertNotPending() {
155 boolean result = !isPending();
157 new Exception(this + ": Assertion failed, expected not pending, got " + statusOrException).printStackTrace();
162 final public boolean assertNotDiscarded() {
163 boolean result = !isDiscarded();
165 new Exception(this + ": Assertion failed, expected not discarded, got " + statusOrException).printStackTrace();
171 public void setResult(Object result) {
172 this.result = result;
175 @SuppressWarnings("unchecked")
177 final public <T> T getResult() {
178 assert(statusOrException != DISCARDED);
183 public void clearResult(QuerySupport support) {
184 setResult(NO_RESULT);
188 final public void addParent(CacheEntry entry) {
190 assert(entry != null);
195 if(p2OrParents == entry) {
200 } else if(p2OrParents == null) {
202 } else if(p2OrParents instanceof QueryIdentityHashSet) {
203 ((QueryIdentityHashSet)p2OrParents).add(entry);
204 ((QueryIdentityHashSet)p2OrParents).purge();
206 CacheEntry tmp = (CacheEntry)p2OrParents;
207 p2OrParents = new QueryIdentityHashSet(2);
208 ((QueryIdentityHashSet)p2OrParents).add(tmp);
209 ((QueryIdentityHashSet)p2OrParents).add(entry);
215 CacheEntry pruneFirstParents() {
222 if(!p1.isDiscarded()) {
224 // First parent is still active
232 // First parent is discarded => look for more parents
233 if(p2OrParents instanceof QueryIdentityHashSet) {
235 QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
236 CacheEntry entry = set.removeDiscarded();
237 if(entry == null) p2OrParents = null;
241 } else if(p2OrParents instanceof CacheEntry) {
243 CacheEntry entry = (CacheEntry)p2OrParents;
244 if(entry.isDiscarded()) {
245 // Second entry is also discarded => all empty
266 final public void removeParent(CacheEntry entry) {
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.");
273 if(p2OrParents == null) {
275 } else if(p2OrParents instanceof QueryIdentityHashSet) {
276 QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
277 int size = set.size();
281 } else if (size == 1) {
282 CacheEntry next = set.iterator().next();
285 } else if(set.size() == 2) {
286 Iterator<CacheEntry> iterator = set.iterator();
287 p1 = iterator.next();
288 p2OrParents = iterator.next();
290 p1 = set.iterator().next();
294 p1 = (CacheEntry)p2OrParents;
298 } else if(p2OrParents.getClass() == QueryIdentityHashSet.class) {
300 QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
301 boolean success = set.remove(entry);
303 throw new Error("CacheEntryBase.removeParent: parent was not found.");
305 assert(set.size() >= 1);
306 if(set.size() == 1) {
307 p2OrParents = set.iterator().next();
311 if(p2OrParents == entry) {
314 throw new Error("CacheEntryBase.removeParent: had 2 parents but neither was removed.");
320 final public boolean hasParents() {
321 assert(statusOrException != DISCARDED);
326 final public Iterable<CacheEntry<?>> getParents(QueryProcessor processor) {
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) {
336 result.add((CacheEntry)p2OrParents);
339 fillImpliedParents(processor, result);
345 CacheEntry getFirstParent(QueryProcessor processor) {
350 boolean moreThanOneParent(QueryProcessor processor) {
351 return p2OrParents != null;
355 int parentCount(QueryProcessor processor) {
356 if(p2OrParents != null) {
357 if(p2OrParents instanceof QueryIdentityHashSet) {
358 return ((QueryIdentityHashSet)p2OrParents).size()+1;
363 return p1 != null ? 1 : 0;
368 protected void fillImpliedParents(QueryProcessor processor, ArrayList<CacheEntry<?>> result) {
371 protected String internalError() {
372 return toString() + " " + statusOrException + " " + result;
376 protected boolean handleException(ReadGraphImpl graph, IntProcedure procedure) throws DatabaseException {
378 procedure.exception(graph, (Throwable)getResult());
385 protected boolean handleException(ReadGraphImpl graph, TripleIntProcedure procedure) throws DatabaseException {
387 procedure.exception(graph, (Throwable)getResult());
394 protected <T> boolean handleException(ReadGraphImpl graph, InternalProcedure<T> procedure) throws DatabaseException {
396 procedure.exception(graph, (Throwable)getResult());
404 boolean isImmutable(ReadGraphImpl graph) throws DatabaseException {
409 boolean shouldBeCollected() {
419 short setLevel(short level) {
420 short existing = this.level;
426 void prepareRecompute(QuerySupport querySupport) {
428 clearResult(querySupport);
437 int setGCStatus(int status) {
443 void setGCStatusFlag(int flag, boolean value) {
452 public Object getOriginalRequest() {
453 // This is the original request for all built-in queries
457 public CacheEntryBase() {