1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.db.impl.query;
\r
14 import java.util.ArrayList;
\r
15 import java.util.Iterator;
\r
17 import org.simantics.db.common.utils.Logger;
\r
18 import org.simantics.db.exception.DatabaseException;
\r
19 import org.simantics.db.impl.DebugPolicy;
\r
20 import org.simantics.db.impl.graph.ReadGraphImpl;
\r
21 import org.simantics.db.impl.procedure.InternalProcedure;
\r
23 abstract public class CacheEntryBase extends CacheEntry {
\r
25 // Default level is something that is not quite a prospect but still allows for ordering within CacheCollectionResult
\r
26 public static final short UNDEFINED_LEVEL = 5;
\r
28 public short level = UNDEFINED_LEVEL;
\r
29 public short age = 0;
\r
30 public int GCStatus = 0;
\r
32 final public static CacheEntryBase[] NONE = new CacheEntryBase[0];
\r
34 static private Object NO_RESULT = new Object();
\r
35 static protected Object INVALID_RESULT = new Object();
\r
38 static protected Object FRESH = new Object() { public String toString() { return "CREATED"; }};
\r
39 // Result is computed - no exception
\r
40 static protected Object READY = new Object() { public String toString() { return "READY"; }};
\r
41 // Computation is under way
\r
42 static protected Object PENDING = new Object() { public String toString() { return "PENDING"; }};
\r
43 // Entry is discarded and is waiting for garbage collect
\r
44 static protected Object DISCARDED = new Object() { public String toString() { return "DISCARDED"; }};
\r
45 // The result has been invalidated
\r
46 static protected Object REFUTED = new Object() { public String toString() { return "REFUTED"; }};
\r
47 // The computation has excepted - the exception is in the result
\r
48 static protected Object EXCEPTED = new Object() { public String toString() { return "EXCEPTED"; }};
\r
50 // This indicates the status of the entry
\r
51 public Object statusOrException = FRESH;
\r
53 private CacheEntry p1 = null;
\r
54 private Object p2OrParents = null;
\r
56 private int hash = 0;
\r
59 final public int hashCode() {
\r
60 if(hash == 0) hash = makeHash();
\r
64 abstract int makeHash();
\r
66 // This can be tested to see if the result is finished
\r
67 private Object result = NO_RESULT;
\r
69 final public boolean isFresh() {
\r
70 return FRESH == statusOrException;
\r
73 public void setReady() {
\r
74 statusOrException = READY;
\r
78 final public boolean isReady() {
\r
79 return READY == statusOrException || EXCEPTED == statusOrException;
\r
83 public void discard() {
\r
84 if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: discarded " + this);
\r
85 statusOrException = DISCARDED;
\r
89 final public boolean isDiscarded() {
\r
90 return DISCARDED == statusOrException;
\r
94 final public void refute() {
\r
95 if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: refuted " + this);
\r
96 statusOrException = REFUTED;
\r
100 final public boolean isRefuted() {
\r
101 return REFUTED == statusOrException;
\r
105 final public void except(Throwable t) {
\r
106 if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: excepted " + this);
\r
107 if(statusOrException != DISCARDED) {
\r
108 statusOrException = EXCEPTED;
\r
111 Logger.defaultLogError("Cache entry got excepted status after being discarded: " + getClass().getSimpleName(), t);
\r
116 final public void checkAndThrow() throws DatabaseException {
\r
118 Throwable throwable = (Throwable)result;
\r
119 if(throwable instanceof DatabaseException) throw (DatabaseException)throwable;
\r
120 else throw new DatabaseException(throwable);
\r
125 final public boolean isExcepted() {
\r
126 return EXCEPTED == statusOrException;
\r
130 final public void setPending() {
\r
131 statusOrException = PENDING;
\r
135 final public boolean isPending() {
\r
136 return PENDING == statusOrException;
\r
139 final public boolean assertPending() {
\r
140 boolean result = isPending();
\r
142 System.err.println("Assertion failed, expected pending, got " + statusOrException);
\r
147 final public boolean assertNotPending() {
\r
148 boolean result = !isPending();
\r
150 new Exception(this + ": Assertion failed, expected not pending, got " + statusOrException).printStackTrace();
\r
155 final public boolean assertNotDiscarded() {
\r
156 boolean result = !isDiscarded();
\r
158 new Exception(this + ": Assertion failed, expected not discarded, got " + statusOrException).printStackTrace();
\r
164 public void setResult(Object result) {
\r
165 this.result = result;
\r
169 final public <T> T getResult() {
\r
170 assert(statusOrException != DISCARDED);
\r
175 public void clearResult(QuerySupport support) {
\r
176 setResult(NO_RESULT);
\r
180 final public void addParent(CacheEntry entry) {
\r
182 assert(entry != null);
\r
187 if(p2OrParents == entry) {
\r
192 } else if(p2OrParents == null) {
\r
193 p2OrParents = entry;
\r
194 } else if(p2OrParents instanceof QueryIdentityHashSet) {
\r
195 ((QueryIdentityHashSet)p2OrParents).add(entry);
\r
196 ((QueryIdentityHashSet)p2OrParents).purge();
\r
198 CacheEntry tmp = (CacheEntry)p2OrParents;
\r
199 p2OrParents = new QueryIdentityHashSet(2);
\r
200 ((QueryIdentityHashSet)p2OrParents).add(tmp);
\r
201 ((QueryIdentityHashSet)p2OrParents).add(entry);
\r
207 CacheEntry pruneFirstParents() {
\r
214 if(!p1.isDiscarded()) {
\r
216 // First parent is still active
\r
224 // First parent is discarded => look for more parents
\r
225 if(p2OrParents instanceof QueryIdentityHashSet) {
\r
227 QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
\r
228 CacheEntry entry = set.removeDiscarded();
\r
229 if(entry == null) p2OrParents = null;
\r
233 } else if(p2OrParents instanceof CacheEntry) {
\r
235 CacheEntry entry = (CacheEntry)p2OrParents;
\r
236 if(entry.isDiscarded()) {
\r
237 // Second entry is also discarded => all empty
\r
238 p2OrParents = null;
\r
242 p2OrParents = null;
\r
258 final public void removeParent(CacheEntry entry) {
\r
261 if(p2OrParents != null) throw new Error("CacheEntryBase.removeParent: corrupted parents (p1 == null, while p2OrParents != null).");
\r
262 else throw new Error("CacheEntryBase.removeParent: no parents.");
\r
265 if(p2OrParents == null) {
\r
267 } else if(p2OrParents instanceof QueryIdentityHashSet) {
\r
268 QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
\r
269 int size = set.size();
\r
272 p2OrParents = null;
\r
273 } else if (size == 1) {
\r
274 CacheEntry next = set.iterator().next();
\r
277 } else if(set.size() == 2) {
\r
278 Iterator<CacheEntry> iterator = set.iterator();
\r
279 p1 = iterator.next();
\r
280 p2OrParents = iterator.next();
\r
282 p1 = set.iterator().next();
\r
286 p1 = (CacheEntry)p2OrParents;
\r
287 p2OrParents = null;
\r
290 } else if(p2OrParents.getClass() == QueryIdentityHashSet.class) {
\r
292 QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
\r
293 boolean success = set.remove(entry);
\r
295 throw new Error("CacheEntryBase.removeParent: parent was not found.");
\r
297 assert(set.size() >= 1);
\r
298 if(set.size() == 1) {
\r
299 p2OrParents = set.iterator().next();
\r
303 if(p2OrParents == entry) {
\r
304 p2OrParents = null;
\r
306 throw new Error("CacheEntryBase.removeParent: had 2 parents but neither was removed.");
\r
312 final public boolean hasParents() {
\r
313 assert(statusOrException != DISCARDED);
\r
318 final public Iterable<CacheEntry> getParents(QueryProcessor processor) {
\r
320 ArrayList<CacheEntry> result = new ArrayList<CacheEntry>();
\r
321 if(p1 != null) result.add(p1);
\r
322 if(p2OrParents != null) {
\r
323 if(p2OrParents instanceof QueryIdentityHashSet) {
\r
324 for(CacheEntry entry : (QueryIdentityHashSet)p2OrParents) {
\r
328 result.add((CacheEntry)p2OrParents);
\r
331 fillImpliedParents(processor, result);
\r
337 CacheEntry getFirstParent(QueryProcessor processor) {
\r
342 boolean moreThanOneParent(QueryProcessor processor) {
\r
343 return p2OrParents != null;
\r
347 int parentCount(QueryProcessor processor) {
\r
348 if(p2OrParents != null) {
\r
349 if(p2OrParents instanceof QueryIdentityHashSet) {
\r
350 return ((QueryIdentityHashSet)p2OrParents).size()+1;
\r
355 return p1 != null ? 1 : 0;
\r
360 protected void fillImpliedParents(QueryProcessor processor, ArrayList<CacheEntry> result) {
\r
364 protected String internalError() {
\r
365 return toString() + " " + statusOrException + " " + result;
\r
369 protected boolean handleException(ReadGraphImpl graph, IntProcedure procedure) {
\r
371 procedure.exception(graph, (Throwable)getResult());
\r
378 protected boolean handleException(ReadGraphImpl graph, TripleIntProcedure procedure) {
\r
380 procedure.exception(graph, (Throwable)getResult());
\r
387 protected <T> boolean handleException(ReadGraphImpl graph, InternalProcedure<T> procedure) {
\r
389 procedure.exception(graph, (Throwable)getResult());
\r
397 boolean isImmutable(ReadGraphImpl graph) throws DatabaseException {
\r
402 boolean shouldBeCollected() {
\r
412 short setLevel(short level) {
\r
413 short existing = this.level;
\r
414 this.level = level;
\r
419 void prepareRecompute(QuerySupport querySupport) {
\r
421 clearResult(querySupport);
\r
429 int getGCStatus() {
\r
434 int setGCStatus(int status) {
\r
440 void setGCStatusFlag(int flag, boolean value) {
\r
449 public Object getOriginalRequest() {
\r
450 // This is the original request for all built-in queries
\r