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.databoard.Bindings;
18 import org.simantics.db.DevelopmentKeys;
19 import org.simantics.db.exception.DatabaseException;
20 import org.simantics.db.impl.graph.ReadGraphImpl;
21 import org.simantics.db.impl.procedure.InternalProcedure;
22 import org.simantics.utils.Development;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
26 public abstract class CacheEntryBase<Procedure> extends CacheEntry<Procedure> {
28 private static final Logger LOGGER = LoggerFactory.getLogger(CacheEntryBase.class);
30 // Default level is something that is not quite a prospect but still allows for ordering within CacheCollectionResult
31 public static final short UNDEFINED_LEVEL = 5;
33 public short level = UNDEFINED_LEVEL;
35 public int GCStatus = 0;
37 final public static CacheEntryBase[] NONE = new CacheEntryBase[0];
39 static Object NO_RESULT = new Object() { public String toString() { return "NO_RESULT"; }};
40 static protected Object INVALID_RESULT = new Object() { public String toString() { return "INVALID_RESULT"; }};
43 // static protected Object FRESH = new Object() { public String toString() { return "CREATED"; }};
44 // Result is computed - no exception
45 static protected Object READY = new Object() { public String toString() { return "READY"; }};
46 // Computation is under way
47 static protected Object PENDING = new Object() { public String toString() { return "PENDING"; }};
48 // Entry is discarded and is waiting for garbage collect
49 static protected Object DISCARDED = new Object() { public String toString() { return "DISCARDED"; }};
50 // The result has been invalidated
51 static protected Object REQUIRES_COMPUTATION = new Object() { public String toString() { return "REFUTED"; }};
52 // The computation has excepted - the exception is in the result
53 static protected Object EXCEPTED = new Object() { public String toString() { return "EXCEPTED"; }};
55 // This indicates the status of the entry
56 public Object statusOrException = REQUIRES_COMPUTATION;
58 private CacheEntry p1 = null;
59 private Object p2OrParents = null;
64 final public int hashCode() {
65 if(hash == 0) hash = makeHash();
69 abstract int makeHash();
71 // This can be tested to see if the result is finished
72 Object result = NO_RESULT;
74 final public boolean isFresh() {
75 return REQUIRES_COMPUTATION == statusOrException;
78 public void setReady() {
79 assert(result != NO_RESULT);
80 statusOrException = READY;
84 final public boolean isReady() {
85 return READY == statusOrException || EXCEPTED == statusOrException;
89 public void discard() {
90 if (Development.DEVELOPMENT) {
91 if(Development.<Boolean>getProperty(DevelopmentKeys.CACHE_ENTRY_STATE, Bindings.BOOLEAN)) {
92 System.err.println("[QUERY STATE]: discarded " + this);
95 statusOrException = DISCARDED;
99 final public boolean isDiscarded() {
100 return DISCARDED == statusOrException;
104 final public void refute() {
105 if (Development.DEVELOPMENT) {
106 if(Development.<Boolean>getProperty(DevelopmentKeys.CACHE_ENTRY_STATE, Bindings.BOOLEAN)) {
107 System.err.println("[QUERY STATE]: refuted " + this);
110 statusOrException = REQUIRES_COMPUTATION;
114 final public boolean isRefuted() {
115 return REQUIRES_COMPUTATION == statusOrException;
119 public void except(Throwable throwable) {
120 if (Development.DEVELOPMENT) {
121 if(Development.<Boolean>getProperty(DevelopmentKeys.CACHE_ENTRY_STATE, Bindings.BOOLEAN)) {
122 System.err.println("[QUERY STATE]: excepted " + this);
125 if(statusOrException != DISCARDED) {
126 statusOrException = EXCEPTED;
129 LOGGER.warn("Cache entry got excepted status after being discarded: " + getClass().getSimpleName(), throwable);
134 final public void checkAndThrow() throws DatabaseException {
136 Throwable throwable = (Throwable)result;
137 if(throwable instanceof DatabaseException) throw (DatabaseException)throwable;
138 else throw new DatabaseException(throwable);
143 final public boolean isExcepted() {
144 return EXCEPTED == statusOrException;
148 public void setPending(QuerySupport querySupport) {
149 statusOrException = PENDING;
150 clearResult(querySupport);
154 final public boolean isPending() {
155 return PENDING == statusOrException;
158 final public boolean requiresComputation() {
159 return REQUIRES_COMPUTATION == statusOrException;
162 final public boolean assertPending() {
163 boolean result = isPending();
165 LOGGER.warn("Assertion failed, expected pending, got " + statusOrException);
170 final public boolean assertNotPending() {
171 boolean result = !isPending();
173 new Exception(this + ": Assertion failed, expected not pending, got " + statusOrException).printStackTrace();
178 final public boolean assertNotDiscarded() {
179 boolean result = !isDiscarded();
181 new Exception(this + ": Assertion failed, expected not discarded, got " + statusOrException).printStackTrace();
187 public void setResult(Object result) {
188 this.result = result;
191 @SuppressWarnings("unchecked")
193 final public <T> T getResult() {
194 assert(statusOrException != DISCARDED);
199 public void clearResult(QuerySupport support) {
200 setResult(NO_RESULT);
204 final public void addParent(CacheEntry entry) {
206 assert(entry != null);
211 if(p2OrParents == entry) {
216 } else if(p2OrParents == null) {
218 } else if(p2OrParents instanceof QueryIdentityHashSet) {
219 ((QueryIdentityHashSet)p2OrParents).add(entry);
220 ((QueryIdentityHashSet)p2OrParents).purge();
222 CacheEntry tmp = (CacheEntry)p2OrParents;
223 p2OrParents = new QueryIdentityHashSet(2);
224 ((QueryIdentityHashSet)p2OrParents).add(tmp);
225 ((QueryIdentityHashSet)p2OrParents).add(entry);
231 CacheEntry pruneFirstParents() {
238 if(!p1.isDiscarded()) {
240 // First parent is still active
248 // First parent is discarded => look for more parents
249 if(p2OrParents instanceof QueryIdentityHashSet) {
251 QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
252 CacheEntry entry = set.removeDiscarded();
253 if(entry == null) p2OrParents = null;
257 } else if(p2OrParents instanceof CacheEntry) {
259 CacheEntry entry = (CacheEntry)p2OrParents;
260 if(entry.isDiscarded()) {
261 // Second entry is also discarded => all empty
282 final public void removeParent(CacheEntry entry) {
285 if(p2OrParents != null) throw new Error("CacheEntryBase.removeParent: corrupted parents (p1 == null, while p2OrParents != null).");
286 else throw new Error("CacheEntryBase.removeParent: no parents.");
289 if(p2OrParents == null) {
291 } else if(p2OrParents instanceof QueryIdentityHashSet) {
292 QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
293 int size = set.size();
297 } else if (size == 1) {
298 CacheEntry next = set.iterator().next();
301 } else if(set.size() == 2) {
302 Iterator<CacheEntry> iterator = set.iterator();
303 p1 = iterator.next();
304 p2OrParents = iterator.next();
306 p1 = set.iterator().next();
310 p1 = (CacheEntry)p2OrParents;
314 } else if(p2OrParents.getClass() == QueryIdentityHashSet.class) {
316 QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
317 boolean success = set.remove(entry);
319 throw new Error("CacheEntryBase.removeParent: parent was not found.");
321 assert(set.size() >= 1);
322 if(set.size() == 1) {
323 p2OrParents = set.iterator().next();
327 if(p2OrParents == entry) {
330 throw new Error("CacheEntryBase.removeParent: had 2 parents but neither was removed.");
336 final public boolean hasParents() {
337 assert(statusOrException != DISCARDED);
342 final public Iterable<CacheEntry<?>> getParents(QueryProcessor processor) {
344 ArrayList<CacheEntry<?>> result = new ArrayList<CacheEntry<?>>();
345 if(p1 != null) result.add(p1);
346 if(p2OrParents != null) {
347 if(p2OrParents instanceof QueryIdentityHashSet) {
348 for(CacheEntry entry : (QueryIdentityHashSet)p2OrParents) {
352 result.add((CacheEntry)p2OrParents);
355 fillImpliedParents(processor, result);
361 CacheEntry getFirstParent(QueryProcessor processor) {
366 boolean moreThanOneParent(QueryProcessor processor) {
367 return p2OrParents != null;
371 int parentCount(QueryProcessor processor) {
372 if(p2OrParents != null) {
373 if(p2OrParents instanceof QueryIdentityHashSet) {
374 return ((QueryIdentityHashSet)p2OrParents).size()+1;
379 return p1 != null ? 1 : 0;
384 protected void fillImpliedParents(QueryProcessor processor, ArrayList<CacheEntry<?>> result) {
387 protected String internalError() {
388 return toString() + " " + statusOrException + " " + result;
392 protected boolean handleException(ReadGraphImpl graph, IntProcedure procedure) throws DatabaseException {
394 procedure.exception(graph, (Throwable)getResult());
401 protected boolean handleException(ReadGraphImpl graph, TripleIntProcedure procedure) throws DatabaseException {
403 procedure.exception(graph, (Throwable)getResult());
410 protected <T> boolean handleException(ReadGraphImpl graph, InternalProcedure<T> procedure) throws DatabaseException {
412 procedure.exception(graph, (Throwable)getResult());
420 boolean isImmutable(ReadGraphImpl graph) throws DatabaseException {
425 boolean shouldBeCollected() {
435 short setLevel(short level) {
436 short existing = this.level;
442 void prepareRecompute(QuerySupport querySupport) {
443 setPending(querySupport);
452 int setGCStatus(int status) {
458 void setGCStatusFlag(int flag, boolean value) {
467 public Object getOriginalRequest() {
468 // This is the original request for all built-in queries
472 public CacheEntryBase() {