--- /dev/null
+/*******************************************************************************\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.procore.cluster;\r
+\r
+import gnu.trove.map.hash.TIntIntHashMap;\r
+import gnu.trove.procedure.TIntIntProcedure;\r
+import gnu.trove.set.hash.TIntHashSet;\r
+\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.exception.ValidationException;\r
+import org.simantics.db.impl.ClusterBase;\r
+import org.simantics.db.impl.ClusterI;\r
+import org.simantics.db.impl.ClusterI.ObjectProcedure;\r
+import org.simantics.db.impl.ClusterI.PredicateProcedure;\r
+import org.simantics.db.impl.ClusterI.Procedure;\r
+import org.simantics.db.impl.ClusterSupport;\r
+import org.simantics.db.impl.Modifier;\r
+import org.simantics.db.impl.Table;\r
+import org.simantics.db.impl.TableFactory;\r
+import org.simantics.db.impl.TableIntAllocatorAdapter;\r
+import org.simantics.db.impl.TableSizeListener;\r
+import org.simantics.db.procore.cluster.TableIntArraySet2.Tables;\r
+\r
+public final class PredicateTable extends Table<int[]> {\r
+ \r
+ final TableIntAllocatorAdapter allocator;\r
+ \r
+ public PredicateTable(TableSizeListener sizeListener, int[] header, int headerBase) {\r
+ super(TableFactory.getIntFactory(), sizeListener, header, headerBase);\r
+ allocator = new TableIntAllocatorAdapter(this);\r
+ }\r
+ public PredicateTable(TableSizeListener sizeListener, int[] header, int headerBase, int[] ints) {\r
+ super(TableFactory.getIntFactory(), sizeListener, header, headerBase, ints);\r
+ allocator = new TableIntAllocatorAdapter(this);\r
+ }\r
+ int createPredicateSet(int[] ps, int[] os)\r
+ throws DatabaseException {\r
+ int hashBase = TableIntArraySet2.create(ps, os, allocator);\r
+ return convertRealIndexToTableIndex(hashBase);\r
+ }\r
+ void deletePredicateSet(int predicateIndex) {\r
+ int hashBase = checkIndexAndGetRealIndex(predicateIndex, 0);\r
+ if (TableIntArraySet2.isArraySet(getTable(), hashBase)) {\r
+ int capacity = TableIntArraySet2.getAllocatedSize(getTable(), hashBase);\r
+ int elementIndex = predicateIndex - TableIntArraySet2.HeaderSize;\r
+ deleteOldElement(elementIndex, capacity);\r
+ } else {\r
+ int capacity = TableIntSet2.getAllocatedSize(getTable(), hashBase);\r
+ int elementIndex = predicateIndex - TableIntSet2.HeaderSize;\r
+ deleteOldElement(elementIndex, capacity);\r
+ }\r
+ }\r
+ public int getPredicateSetSize(int predicateIndex) {\r
+ int hashBase = checkIndexAndGetRealIndex(predicateIndex, 0);\r
+ if (TableIntArraySet2.isArraySet(getTable(), hashBase))\r
+ return TableIntArraySet2.getSize(getTable(), hashBase);\r
+ else\r
+ return TableIntSet2.getSize(getTable(), hashBase);\r
+ }\r
+ public int getObjectIndex(int predicateIndex, int pRef) {\r
+ int hashBase = checkIndexAndGetRealIndex(predicateIndex, 0);\r
+ if (TableIntArraySet2.isArraySet(table, hashBase))\r
+ return TableIntArraySet2.get(table, hashBase, pRef);\r
+ else {\r
+ return TableIntSet2.get(table, hashBase, pRef);\r
+ }\r
+ }\r
+ private int addPredicateArray(int predicateIndex, int hashBase, int pReference, int oReference, ObjectTable ot)\r
+ throws DatabaseException {\r
+ int newHashBase;\r
+ int objectIndex = TableIntArraySet2.get(getTable(), hashBase, pReference);\r
+ if (0 == objectIndex) {\r
+ newHashBase = TableIntArraySet2.addInt(getTable(), hashBase, pReference, oReference, allocator);\r
+ } else if (ClusterTraits.statementIndexIsDirect(objectIndex)) {\r
+ int oRef = objectIndex;\r
+ if (oRef == oReference) {\r
+ return 0; // old direct object\r
+ }\r
+ objectIndex = ot.createObjectSet(oRef, oReference);\r
+ assert(0 != objectIndex);\r
+ int newObjectIndex = ClusterTraits.statementIndexMake(objectIndex);\r
+ newHashBase = TableIntArraySet2.addInt(getTable(), hashBase, pReference, newObjectIndex, allocator);\r
+ } else {\r
+ int newObjectIndex = ot.addObject(ClusterTraits.statementIndexGet(objectIndex), oReference);\r
+ if (0 == newObjectIndex)\r
+ return 0; // old indirect object\r
+ newObjectIndex = ClusterTraits.statementIndexMake(newObjectIndex);\r
+ newHashBase = TableIntArraySet2.addInt(getTable(), hashBase, pReference, newObjectIndex, allocator);\r
+ if (newHashBase == 0)\r
+ return hashBase;\r
+ }\r
+ int TABLE_SIZE = TableIntArraySet2.getSize(getTable(), newHashBase);\r
+ if (TABLE_SIZE > 5)\r
+ return convertToPredicateSet(predicateIndex, newHashBase);\r
+ return newHashBase;\r
+ }\r
+ private int convertToPredicateSet(int predicateIndex, int hashBase) {\r
+ Tables tables = TableIntArraySet2.getInts(getTable(), hashBase);\r
+ this.deletePredicateSet(predicateIndex);\r
+ int newHashBase = TableIntSet2.create(tables.keys, tables.vals, allocator);\r
+ assert(0 != newHashBase);\r
+ return newHashBase;\r
+ }\r
+ private int addPredicateSet(int hashBase, int pReference, int oReference, ObjectTable ot)\r
+ throws DatabaseException {\r
+ int objectIndex = TableIntSet2.get(getTable(), hashBase, pReference);\r
+ int newHashBase;\r
+ if (0 == objectIndex) {\r
+ newHashBase = TableIntSet2.addInt(getTable(), hashBase, pReference, oReference, allocator);\r
+ } else if (ClusterTraits.statementIndexIsDirect(objectIndex)) {\r
+ int oRef = objectIndex;\r
+ if (oRef == oReference) {\r
+ return 0; // old direct object\r
+ }\r
+ objectIndex = ot.createObjectSet(oRef, oReference);\r
+ assert(0 != objectIndex);\r
+ int newObjectIndex = ClusterTraits.statementIndexMake(objectIndex);\r
+ newHashBase = TableIntSet2.addInt(getTable(), hashBase, pReference, newObjectIndex, allocator);\r
+ assert(0 != newHashBase);\r
+ } else {\r
+ int newObjectIndex = ot.addObject(ClusterTraits.statementIndexGet(objectIndex), oReference);\r
+ if (0 == newObjectIndex)\r
+ return 0; // old indirect object\r
+ int stmIndex = ClusterTraits.statementIndexMake(newObjectIndex);\r
+ newHashBase = TableIntSet2.addInt(getTable(), hashBase, pReference, stmIndex, allocator);\r
+ if (newHashBase == 0)\r
+ return hashBase; // new object added to old predicate (set)\r
+ }\r
+ return newHashBase;\r
+ }\r
+ /**\r
+ * @param predicateIndex\r
+ * @param pReference\r
+ * @return zero if element was not added or predicate index.\r
+ * Predicate index will change if new space is allocated.\r
+ */\r
+ public int addPredicate(int predicateIndex, int pReference, int oReference, ObjectTable ot)\r
+ throws DatabaseException {\r
+ int hashBase = checkIndexAndGetRealIndex(predicateIndex, 0);\r
+ int newHashBase;\r
+ if (TableIntArraySet2.isArraySet(getTable(), hashBase))\r
+ newHashBase = addPredicateArray(predicateIndex, hashBase, pReference, oReference, ot);\r
+ else\r
+ newHashBase = addPredicateSet(hashBase, pReference, oReference, ot);\r
+ if (0 == newHashBase)\r
+ return 0; // not modified\r
+ return convertRealIndexToTableIndex(newHashBase);\r
+ }\r
+ public enum Status {\r
+ NothingRemoved,\r
+ ObjectRemoved,\r
+ PredicateRemoved;\r
+ }\r
+ /**\r
+ * @param predicateIndex\r
+ * @param oResourceIndex\r
+ * @return null if nothing was removed. Status if either object or both\r
+ * object and predicate were removed. \r
+ */\r
+ public Status removePredicate(int predicateIndex, int pReference, int oReference, ObjectTable ot)\r
+ throws DatabaseException {\r
+ int hashBase = checkIndexAndGetRealIndex(predicateIndex, 0);\r
+ int[] table = getTable();\r
+ if (TableIntArraySet2.isArraySet(table, hashBase)) {\r
+ int objectIndex = TableIntArraySet2.get(table, hashBase, pReference);\r
+ if (0 == objectIndex) {\r
+ return Status.NothingRemoved;\r
+ } else if (ClusterTraits.statementIndexIsDirect(objectIndex)) {\r
+ int oRef = objectIndex;\r
+ if (oRef != oReference)\r
+ return Status.NothingRemoved;\r
+ if (TableIntArraySet2.removeInt(table, hashBase, pReference))\r
+ return Status.PredicateRemoved;\r
+ else\r
+ throw new DatabaseException("Internal error during remove.");\r
+ } else {\r
+ int oIndex = ClusterTraits.statementIndexGet(objectIndex);\r
+ int nO = ot.getObjectSetSize(oIndex);\r
+ if (nO < 1)\r
+ throw new DatabaseException("Illegal object set size="+nO);\r
+ int nObject = ot.removeObject(objectIndex, oReference);\r
+ if (nObject == 0) {\r
+ ot.deleteObjectSet(oIndex);\r
+ if (TableIntArraySet2.removeInt(table, hashBase, pReference))\r
+ return Status.PredicateRemoved;\r
+ else\r
+ throw new DatabaseException("Internal error during remove (2).");\r
+ } else if (nO == nObject)\r
+ return Status.NothingRemoved;\r
+ else\r
+ return Status.ObjectRemoved;\r
+ }\r
+ } else {\r
+ int objectIndex = TableIntSet2.get(table, hashBase, pReference);\r
+ if (0 == objectIndex) {\r
+ return Status.NothingRemoved;\r
+ } else if (ClusterTraits.statementIndexIsDirect(objectIndex)) {\r
+ int oRef = objectIndex;\r
+ if (oRef != oReference)\r
+ return Status.NothingRemoved;\r
+ if (TableIntSet2.removeInt(table, hashBase, pReference))\r
+ return Status.PredicateRemoved;\r
+ else\r
+ throw new DatabaseException("Internal error during remove (3).");\r
+ } else {\r
+ int oIndex = ClusterTraits.statementIndexGet(objectIndex);\r
+ int nO = ot.getObjectSetSize(oIndex);\r
+ if (nO < 1)\r
+ throw new DatabaseException("Illegal object set size="+nO);\r
+ int nObject = ot.removeObject(objectIndex, oReference);\r
+ if (nObject == 0) {\r
+ ot.deleteObjectSet(oIndex);\r
+ if (TableIntSet2.removeInt(table, hashBase, pReference))\r
+ return Status.PredicateRemoved;\r
+ else\r
+ throw new DatabaseException("Internal error during remove (4).");\r
+ } else if (nO == nObject)\r
+ return Status.NothingRemoved;\r
+ else\r
+ return Status.ObjectRemoved;\r
+ }\r
+ }\r
+ }\r
+ public <Context> boolean foreachPredicate(int predicateIndex\r
+ , ClusterI.PredicateProcedure<Context> procedure\r
+ , Context context, ClusterSupport support, Modifier modifier)\r
+ throws DatabaseException {\r
+ final int hashBase = checkIndexAndGetRealIndex(predicateIndex, 0);\r
+ boolean ret;\r
+ if (TableIntArraySet2.isArraySet(getTable(), hashBase))\r
+ ret = TableIntArraySet2.foreachInt(getTable(), hashBase, procedure, context, modifier);\r
+ else\r
+ ret = TableIntSet2.foreachInt(getTable(), hashBase, procedure, context, modifier);\r
+ return ret;\r
+ }\r
+ private void checkEntry(ClusterBase cluster, int[] table, int index)\r
+ throws DatabaseException {\r
+ assert(ClusterTraits.statementIndexIsDirect(table[index]));\r
+ int dr = table[index];\r
+ cluster.checkDirectReference(dr);\r
+ assert(table[index+1] != 0);\r
+ if (ClusterTraits.statementIndexIsDirect(table[index+1])) {\r
+ cluster.checkDirectReference(table[index+1]);\r
+ } else {\r
+ cluster.checkObjectSetReference(table[index+1]);\r
+ }\r
+ }\r
+ private TIntHashSet checkIndexSet = null;\r
+ public void check(ClusterBase cluster)\r
+ throws DatabaseException {\r
+ if (null == checkIndexSet)\r
+ checkIndexSet = new TIntHashSet();\r
+ else\r
+ checkIndexSet.clear();\r
+ int count = 0;\r
+ int[] table = getTable();\r
+ int ps = getHeader().getOffset() + ZERO_SHIFT;\r
+ int pe = ps + getTableSize();\r
+ for (int p=ps; p<pe;) {\r
+ int cap = p++;\r
+ if (table[cap] >= 0) {\r
+ int use = p++;\r
+ int fre = p++;\r
+ int max = p++;\r
+ assert(table[cap] >= table[use] + table[fre]);\r
+ assert(table[max] == table[cap] >> 1);\r
+ assert(table[max]+1 >= table[use]);\r
+ checkIndexSet.add(p - ps);\r
+ for (int e = p + table[cap]*2; p<e; p+=2) {\r
+ if (!IntHashTrait.isFull(table[p])) \r
+ assert(table[p+1]==0);\r
+ else\r
+ checkEntry(cluster, table, p);\r
+ }\r
+ } else {\r
+ final int size = -table[cap];\r
+ assert(size > 0);\r
+ boolean free = false;\r
+ checkIndexSet.add(p - ps);\r
+ for (int e = p + size*2; p<e; p+=2) {\r
+ if (free) {\r
+ assert(table[p] == 0);\r
+ assert(table[p+1] == 0);\r
+ }\r
+ else if (table[p] == 0) {\r
+ assert(table[p+1] == 0);\r
+ free = true;\r
+ } else\r
+ checkEntry(cluster, table, p);\r
+ }\r
+ }\r
+ count++;\r
+ }\r
+ assert(getHeader().getCount() <= count); // deleted objects are not recognized\r
+ }\r
+ public final void checkPredicateSetIndex(ClusterBase cluster, int i)\r
+ throws DatabaseException {\r
+ if (null == checkIndexSet)\r
+ check(cluster); // builds checkIndexSet\r
+ if (!checkIndexSet.contains(i-ZERO_SHIFT))\r
+ throw new ValidationException("Illegal predicate set index=" + i);\r
+ }\r
+ public void printDebugInfo() {\r
+ //int count = 0;\r
+ int[] table = getTable();\r
+ int ps = getHeader().getOffset() + ZERO_SHIFT;\r
+ int pe = ps + getTableSize();\r
+ TIntIntHashMap stat = new TIntIntHashMap();\r
+ TIntIntHashMap stat2 = new TIntIntHashMap();\r
+ for (int p=ps; p<pe;) {\r
+ int cap = p++;\r
+ if (table[cap] >= 0) {\r
+ int use = p++;\r
+ int fre = p++;\r
+ int max = p++;\r
+ assert(table[cap] >= table[use] + table[fre]);\r
+ assert(table[max] == table[cap] >> 1);\r
+ p += table[cap]*2;\r
+ int val = stat.get(table[use]) + 1;\r
+ stat.put(table[use], val);\r
+ } else {\r
+ final int size = -table[cap];\r
+ int val = stat2.get(size) + 1;\r
+ stat2.put(size, val);\r
+ p += size*2;\r
+ }\r
+ //count++;\r
+ }\r
+ //assert(getHeader().getCount() == count);\r
+ stat.forEachEntry(new TIntIntProcedure() {\r
+ @Override\r
+ public boolean execute(int a, int b) {\r
+ System.out.println("predicate set capacity " + a + " instance count " + b);\r
+ return true;\r
+ }\r
+ });\r
+ stat2.forEachEntry(new TIntIntProcedure() {\r
+ @Override\r
+ public boolean execute(int a, int b) {\r
+ System.out.println("predicate array set capacity " + a + " instance count " + b);\r
+ return true;\r
+ }\r
+ });\r
+ }\r
+ @Override\r
+ public <Context> boolean foreach(int setIndex, Procedure procedure, Context context,\r
+ ClusterSupport support, Modifier modifier) throws DatabaseException {\r
+ return foreachPredicate(setIndex, (PredicateProcedure<Context>)procedure, context, support, modifier);\r
+ }\r
+}\r