]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntryBase.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.db.impl / src / org / simantics / db / impl / query / CacheEntryBase.java
diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntryBase.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntryBase.java
new file mode 100644 (file)
index 0000000..953f915
--- /dev/null
@@ -0,0 +1,454 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.db.impl.query;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+\r
+import org.simantics.db.common.utils.Logger;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.impl.DebugPolicy;\r
+import org.simantics.db.impl.graph.ReadGraphImpl;\r
+import org.simantics.db.impl.procedure.InternalProcedure;\r
+\r
+abstract public class CacheEntryBase extends CacheEntry {\r
+\r
+       // Default level is something that is not quite a prospect but still allows for ordering within CacheCollectionResult \r
+       public static final short UNDEFINED_LEVEL = 5;\r
+       \r
+       public short level = UNDEFINED_LEVEL;\r
+       public short age = 0;\r
+       public int GCStatus = 0;\r
+       \r
+    final public static CacheEntryBase[] NONE = new CacheEntryBase[0];\r
+\r
+       static private Object NO_RESULT = new Object();\r
+       static protected Object INVALID_RESULT = new Object();\r
+       \r
+       // Just created\r
+    static protected Object FRESH = new Object() { public String toString() { return "CREATED"; }};\r
+    // Result is computed - no exception\r
+    static protected Object READY = new Object() { public String toString() { return "READY"; }};\r
+    // Computation is under way\r
+    static protected Object PENDING = new Object() { public String toString() { return "PENDING"; }};\r
+    // Entry is discarded and is waiting for garbage collect\r
+    static protected Object DISCARDED = new Object() { public String toString() { return "DISCARDED"; }};\r
+    // The result has been invalidated\r
+    static protected Object REFUTED = new Object() { public String toString() { return "REFUTED"; }};\r
+    // The computation has excepted - the exception is in the result\r
+    static protected Object EXCEPTED = new Object() { public String toString() { return "EXCEPTED"; }};\r
+\r
+    // This indicates the status of the entry\r
+    public Object statusOrException = FRESH;\r
+    \r
+    private CacheEntry p1 = null;\r
+    private Object p2OrParents = null;\r
+    \r
+    private int hash = 0;\r
+    \r
+    @Override\r
+    final public int hashCode() {\r
+       if(hash == 0) hash = makeHash();\r
+       return hash;\r
+    }\r
+    \r
+    abstract int makeHash();\r
+    \r
+    // This can be tested to see if the result is finished\r
+    private Object result = NO_RESULT;\r
+    \r
+    final public boolean isFresh() {\r
+       return FRESH == statusOrException;\r
+    }\r
+\r
+    public void setReady() {\r
+       statusOrException = READY;\r
+    }\r
+\r
+    @Deprecated\r
+    final public boolean isReady() {\r
+       return READY == statusOrException || EXCEPTED == statusOrException;\r
+    }\r
+    \r
+    @Override\r
+    public void discard() {\r
+       if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: discarded " + this);\r
+       statusOrException = DISCARDED;\r
+    }\r
+    \r
+    @Override\r
+    final public boolean isDiscarded() {\r
+        return DISCARDED == statusOrException;\r
+    }\r
+    \r
+    @Override\r
+    final public void refute() {\r
+       if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: refuted " + this);\r
+       statusOrException = REFUTED;\r
+    }\r
+    \r
+    @Override\r
+    final public boolean isRefuted() {\r
+        return REFUTED == statusOrException;\r
+    }\r
+\r
+    @Override\r
+    final public void except(Throwable t) {\r
+       if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: excepted " + this);\r
+       if(statusOrException != DISCARDED) {\r
+               statusOrException = EXCEPTED;\r
+               result = t;\r
+       } else {\r
+               Logger.defaultLogError("Cache entry got excepted status after being discarded: " + getClass().getSimpleName(), t);\r
+               result = t;\r
+       }\r
+    }\r
+    \r
+    final public void checkAndThrow() throws DatabaseException {\r
+       if(isExcepted()) {\r
+           Throwable throwable = (Throwable)result;\r
+           if(throwable instanceof DatabaseException) throw (DatabaseException)throwable;\r
+           else throw new DatabaseException(throwable);\r
+       }\r
+    }\r
+    \r
+    @Override\r
+    final public boolean isExcepted() {\r
+        return EXCEPTED == statusOrException;\r
+    }\r
+\r
+    @Override\r
+    final public void setPending() {\r
+       statusOrException = PENDING;\r
+    }\r
+    \r
+    @Override\r
+    final public boolean isPending() {\r
+        return PENDING == statusOrException;\r
+    }\r
+    \r
+    final public boolean assertPending() {\r
+       boolean result = isPending();\r
+       if(!result) {\r
+               System.err.println("Assertion failed, expected pending, got " + statusOrException);\r
+       }\r
+       return result;\r
+    }\r
+\r
+    final public boolean assertNotPending() {\r
+       boolean result = !isPending();\r
+       if(!result) {\r
+               new Exception(this +  ": Assertion failed, expected not pending, got " + statusOrException).printStackTrace();\r
+       }\r
+       return result;\r
+    }\r
+\r
+    final public boolean assertNotDiscarded() {\r
+       boolean result = !isDiscarded();\r
+       if(!result) {\r
+               new Exception(this +  ": Assertion failed, expected not discarded, got " + statusOrException).printStackTrace();\r
+       }\r
+       return result;\r
+    }\r
+\r
+    @Override\r
+    public void setResult(Object result) {\r
+       this.result = result;\r
+    }\r
+    \r
+    @Override\r
+    final public <T> T getResult() {\r
+        assert(statusOrException != DISCARDED);\r
+        return (T)result;\r
+    }\r
+    \r
+    @Override\r
+    public void clearResult(QuerySupport support) {\r
+       setResult(NO_RESULT);\r
+    }\r
+    \r
+    @Override\r
+    final public void addParent(CacheEntry entry) {\r
+        \r
+        assert(entry != null);\r
+        \r
+        if(p1 == entry) {\r
+               return;\r
+        }\r
+        if(p2OrParents == entry) {\r
+               return;\r
+        }\r
+        if(p1 == null) {\r
+               p1 = entry;\r
+        } else if(p2OrParents == null) {\r
+               p2OrParents = entry;\r
+        } else if(p2OrParents instanceof QueryIdentityHashSet) {\r
+            ((QueryIdentityHashSet)p2OrParents).add(entry);\r
+            ((QueryIdentityHashSet)p2OrParents).purge();\r
+        } else {\r
+            CacheEntry tmp = (CacheEntry)p2OrParents;\r
+            p2OrParents = new QueryIdentityHashSet(2);\r
+            ((QueryIdentityHashSet)p2OrParents).add(tmp);\r
+            ((QueryIdentityHashSet)p2OrParents).add(entry);\r
+        }\r
+        \r
+    }\r
+\r
+    @Override\r
+    CacheEntry pruneFirstParents() {\r
+\r
+       if(p1 == null) {\r
+               // No parents\r
+               return null;\r
+       }\r
+       \r
+       if(!p1.isDiscarded()) {\r
+               \r
+               // First parent is still active\r
+               return p1;\r
+               \r
+       } else {\r
+               \r
+               // Clear p1\r
+               p1 = null;\r
+               \r
+               // First parent is discarded => look for more parents\r
+               if(p2OrParents instanceof QueryIdentityHashSet) {\r
+\r
+                       QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;\r
+                       CacheEntry entry = set.removeDiscarded();\r
+                       if(entry == null) p2OrParents = null;\r
+                       p1 = entry;\r
+                       return p1;\r
+\r
+               } else if(p2OrParents instanceof CacheEntry) {\r
+                       \r
+                       CacheEntry entry = (CacheEntry)p2OrParents;\r
+                       if(entry.isDiscarded()) {\r
+                               // Second entry is also discarded => all empty\r
+                               p2OrParents = null;\r
+                               return null;\r
+                       } else {\r
+                               p1 = entry;\r
+                               p2OrParents = null;\r
+                               return p1;\r
+                       }\r
+                       \r
+               } else {\r
+               \r
+                       // Nothing left\r
+                       return null;\r
+                       \r
+               }\r
+               \r
+       }\r
+        \r
+    }\r
+    \r
+    @Override\r
+    final public void removeParent(CacheEntry entry) {\r
+       \r
+        if(p1 == null) {\r
+            if(p2OrParents != null) throw new Error("CacheEntryBase.removeParent: corrupted parents (p1 == null, while p2OrParents != null).");\r
+            else throw new Error("CacheEntryBase.removeParent: no parents.");\r
+        }\r
+        if(p1 == entry) {\r
+            if(p2OrParents == null) {\r
+                p1 = null;\r
+            } else if(p2OrParents instanceof QueryIdentityHashSet) {\r
+                QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;\r
+                int size = set.size();\r
+                if(size == 0) {\r
+                    p1 = null;\r
+                    p2OrParents = null;\r
+                } else if (size == 1) {\r
+                    CacheEntry next = set.iterator().next();\r
+                    p1 = next;\r
+                    set = null;\r
+                } else if(set.size() == 2) {\r
+                    Iterator<CacheEntry> iterator = set.iterator();\r
+                    p1 = iterator.next();\r
+                    p2OrParents = iterator.next();\r
+                } else {\r
+                    p1 = set.iterator().next();\r
+                    set.remove(p1);\r
+                }\r
+            } else {\r
+                p1 = (CacheEntry)p2OrParents;\r
+                p2OrParents = null;\r
+            }\r
+            \r
+        } else if(p2OrParents.getClass() == QueryIdentityHashSet.class) {\r
+            \r
+            QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;\r
+            boolean success = set.remove(entry);\r
+            if(!success) {\r
+               throw new Error("CacheEntryBase.removeParent: parent was not found.");\r
+            }\r
+            assert(set.size() >= 1);\r
+            if(set.size() == 1) {\r
+                p2OrParents = set.iterator().next();\r
+            }\r
+            \r
+        } else {\r
+            if(p2OrParents == entry) {\r
+                p2OrParents = null;\r
+            } else {\r
+                throw new Error("CacheEntryBase.removeParent: had 2 parents but neither was removed.");\r
+            }\r
+        }\r
+    }\r
+\r
+    @Override\r
+    final public boolean hasParents() {\r
+        assert(statusOrException != DISCARDED);\r
+        return p1 != null;\r
+    }\r
+    \r
+    @Override\r
+       final public Iterable<CacheEntry> getParents(QueryProcessor processor) {\r
+\r
+               ArrayList<CacheEntry> result = new ArrayList<CacheEntry>();\r
+               if(p1 != null) result.add(p1);\r
+               if(p2OrParents != null) {\r
+               if(p2OrParents instanceof QueryIdentityHashSet) {\r
+                       for(CacheEntry entry : (QueryIdentityHashSet)p2OrParents) {\r
+                               result.add(entry);\r
+                       }\r
+               } else {\r
+                       result.add((CacheEntry)p2OrParents);\r
+               }\r
+               }\r
+               fillImpliedParents(processor, result);\r
+               return result;\r
+               \r
+       }\r
+    \r
+    @Override\r
+    CacheEntry getFirstParent(QueryProcessor processor) {\r
+       return p1;\r
+    }\r
+    \r
+    @Override\r
+    boolean moreThanOneParent(QueryProcessor processor) {\r
+       return p2OrParents != null;\r
+    }\r
+    \r
+    @Override\r
+    int parentCount(QueryProcessor processor) {\r
+       if(p2OrParents != null) {\r
+               if(p2OrParents instanceof QueryIdentityHashSet) {\r
+                       return ((QueryIdentityHashSet)p2OrParents).size()+1;\r
+               } else {\r
+                       return 2;\r
+               }\r
+       } else {\r
+               return p1 != null ? 1 : 0;\r
+       }\r
+       \r
+    }\r
+    \r
+    protected void fillImpliedParents(QueryProcessor processor, ArrayList<CacheEntry> result) {\r
+       \r
+    }\r
+    \r
+    protected String internalError() {\r
+       return toString() + " " + statusOrException + " " + result;\r
+    }\r
+\r
+    \r
+    protected boolean handleException(ReadGraphImpl graph, IntProcedure procedure) {\r
+       if(isExcepted()) {\r
+               procedure.exception(graph, (Throwable)getResult());\r
+               return true;\r
+       } else {\r
+               return false;\r
+       }\r
+    }\r
+    \r
+    protected boolean handleException(ReadGraphImpl graph, TripleIntProcedure procedure) {\r
+       if(isExcepted()) {\r
+               procedure.exception(graph, (Throwable)getResult());\r
+               return true;\r
+       } else {\r
+               return false;\r
+       }\r
+    }\r
+\r
+    protected <T> boolean handleException(ReadGraphImpl graph, InternalProcedure<T> procedure) {\r
+       if(isExcepted()) {\r
+               procedure.exception(graph, (Throwable)getResult());\r
+               return true;\r
+       } else {\r
+               return false;\r
+       }\r
+    }\r
+    \r
+    @Override\r
+    boolean isImmutable(ReadGraphImpl graph) throws DatabaseException {\r
+       return false;\r
+    }\r
+    \r
+    @Override\r
+    boolean shouldBeCollected() {\r
+       return true;\r
+    }\r
+    \r
+    @Override\r
+    short getLevel() {\r
+       return level;\r
+    }\r
+    \r
+    @Override\r
+    short setLevel(short level) {\r
+       short existing = this.level;\r
+       this.level = level;\r
+       return existing;\r
+    }\r
+    \r
+    @Override\r
+    void prepareRecompute(QuerySupport querySupport) {\r
+               setPending();\r
+               clearResult(querySupport);\r
+    }\r
+    \r
+    /*\r
+     * \r
+     * \r
+     */\r
+    @Override\r
+    int getGCStatus() {\r
+       return GCStatus;\r
+    }\r
+    \r
+    @Override\r
+    int setGCStatus(int status) {\r
+       GCStatus = status;\r
+       return GCStatus;\r
+    }\r
+    \r
+    @Override\r
+    void setGCStatusFlag(int flag, boolean value) {\r
+       if(value) {\r
+               GCStatus |= flag;\r
+       } else {\r
+               GCStatus &= ~flag;\r
+       }\r
+    }\r
+    \r
+    @Override\r
+    public Object getOriginalRequest() {\r
+       // This is the original request for all built-in queries\r
+       return getQuery();\r
+    }\r
+    \r
+}\r