--- /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.CompleteTypeEnum;\r
+import org.simantics.db.impl.ClusterI.ObjectProcedure;\r
+import org.simantics.db.impl.ClusterI.Procedure;\r
+import org.simantics.db.impl.ClusterSupport;\r
+import org.simantics.db.impl.ClusterTraitsBase;\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.TableIntArraySet.Ints;\r
+\r
+public class CompleteTable extends Table<int[]> {\r
+ public CompleteTable(TableSizeListener sizeListener, int[] header, int headerBase) {\r
+ super(TableFactory.getIntFactory(), sizeListener, header, headerBase);\r
+ }\r
+ public CompleteTable(TableSizeListener sizeListener, int[] header, int headerBase, int[] ints) {\r
+ super(TableFactory.getIntFactory(), sizeListener, header, headerBase, ints);\r
+ }\r
+ final int createCompleteArraySet(int o1, int o2)\r
+ throws DatabaseException {\r
+ if (0 == o1 || o1 == o2)\r
+ throw new DatabaseException("Illegal argument to createObejctArray");\r
+ int[] obs = new int[2];\r
+ obs[0] = o1;\r
+ obs[1] = o2;\r
+ int hashBase = TableIntArraySet.create(obs, new TableIntAllocatorAdapter(this));\r
+ return convertRealIndexToTableIndex(hashBase);\r
+ }\r
+ final void deleteCompleteSet(int index)\r
+ throws DatabaseException {\r
+ int hashBase = checkIndexAndGetRealIndex(index, 0);\r
+ if (TableIntArraySet.isArraySet(getTable(), hashBase)) {\r
+ int capacity = TableIntArraySet.getAllocatedSize(getTable(), hashBase);\r
+ int elementIndex = index - TableIntArraySet.HeaderSize;\r
+ deleteOldElement(elementIndex, capacity);\r
+ } else {\r
+ int capacity = TableIntSet.getAllocatedSize(getTable(), hashBase);\r
+ int elementIndex = index - TableIntSet.HeaderSize;\r
+ deleteOldElement(elementIndex, capacity);\r
+ }\r
+ }\r
+ final int getCompleteSetSize(int objectIndex) {\r
+ int hashBase = checkIndexAndGetRealIndex(objectIndex, 0);\r
+ if (TableIntArraySet.isArraySet(getTable(), hashBase))\r
+ return TableIntArraySet.getSize(getTable(), hashBase);\r
+ else\r
+ return TableIntSet.getSize(getTable(), hashBase);\r
+ }\r
+ /**\r
+ * @param setIndex\r
+ * @param oResourceIndex\r
+ * @return zero if complete already in the set else index of the set\r
+ */\r
+ final int addComplete(int setIndex, int oResourceIndex)\r
+ throws DatabaseException {\r
+ int hashBase = checkIndexAndGetRealIndex(setIndex, 0);\r
+ int newHashBase;\r
+ if (TableIntArraySet.isArraySet(getTable(), hashBase)) {\r
+ if (TableIntArraySet.getSize(getTable(), hashBase) < 5)\r
+ newHashBase = TableIntArraySet.addInt(getTable(), hashBase, oResourceIndex, new TableIntAllocatorAdapter(this));\r
+ else {\r
+ Ints ints = TableIntArraySet.getIntsIfValueNotFound(getTable(), hashBase, oResourceIndex);\r
+ if (ints.found)\r
+ return 0; // old object, not modified \r
+ this.deleteCompleteSet(setIndex);\r
+ newHashBase = TableIntSet.create(ints.ints, new TableIntAllocatorAdapter(this));\r
+ assert(0 != newHashBase);\r
+ }\r
+ } else\r
+ newHashBase = TableIntSet.addInt(getTable(), hashBase, oResourceIndex, new TableIntAllocatorAdapter(this));\r
+ if (0 == newHashBase)\r
+ return 0; // old object, not modified\r
+ int ni = convertRealIndexToTableIndex(newHashBase);\r
+ return ni;\r
+ }\r
+ final int removeLast(int setIndex)\r
+ throws DatabaseException {\r
+ int hashBase = checkIndexAndGetRealIndex(setIndex, 0);\r
+ int[] table = getTable();\r
+ int ref;\r
+ if (TableIntArraySet.isArraySet(table, hashBase))\r
+ ref = TableIntArraySet.removeIntLast(table, hashBase);\r
+ else {\r
+ ref = TableIntSet.removeIntLast(table, hashBase);\r
+ }\r
+ deleteCompleteSet(setIndex);\r
+ return ref;\r
+ }\r
+ /**\r
+ * @param setIndex\r
+ * @param oResourceIndex\r
+ * @return number of objects after removal.\r
+ */\r
+ final int removeComplete(int setIndex, int oResourceIndex)\r
+ throws DatabaseException {\r
+ int hashBase = checkIndexAndGetRealIndex(setIndex, 0);\r
+ int[] table = getTable();\r
+ if (TableIntArraySet.isArraySet(table, hashBase))\r
+ return TableIntArraySet.removeInt(table, hashBase, oResourceIndex);\r
+ else {\r
+ TableIntSet.removeInt(table, hashBase, oResourceIndex);\r
+ return TableIntSet.getSize(table, hashBase);\r
+ }\r
+ }\r
+ final public <Context> boolean foreachComplete(final int setIndex,\r
+ final ClusterI.ObjectProcedure<Context> procedure, final Context context, final ClusterSupport support,\r
+ final Modifier modifier) throws DatabaseException {\r
+ final int hashBase = checkIndexAndGetRealIndex(setIndex, 0);\r
+ boolean ret;\r
+ if (TableIntArraySet.isArraySet(getTable(), hashBase))\r
+ ret = TableIntArraySet.foreachInt(getTable(), hashBase, procedure, context, modifier);\r
+ else\r
+ ret = TableIntSet.foreachInt(getTable(), hashBase, procedure, context, modifier);\r
+ return ret;\r
+ }\r
+ public <Context> boolean foreachPredicate(int setIndex,\r
+ ClusterI.PredicateProcedure<Context> procedure,\r
+ Context context, ClusterSupport support, Modifier modifier)\r
+ throws DatabaseException {\r
+ ForeachPredicate<Context> t = new ForeachPredicate<Context>(procedure, support, modifier);\r
+ return foreachComplete(setIndex, t, context, null, null);\r
+ }\r
+ \r
+ public <Context> boolean foreachObject(int setIndex,\r
+ ClusterI.ObjectProcedure<Context> procedure,\r
+ Context context, ClusterSupport support, Modifier modifier,\r
+ ClusterI.CompleteTypeEnum completeType)\r
+ throws DatabaseException {\r
+ ForeachObject<Context> t = new ForeachObject<Context>\r
+ (procedure, support, modifier, completeType);\r
+ return foreachComplete(setIndex, t, context, null, null);\r
+ }\r
+ \r
+ private void checkEntry(ClusterBase cluster, int[] table, int index)\r
+ throws DatabaseException {\r
+ ClusterI.CompleteTypeEnum type = ClusterTraits.completeReferenceGetType(table[index]);\r
+ if (type == CompleteTypeEnum.NotComplete)\r
+ throw new ValidationException("Illegal CompleteTable entry type. Entry=" + table[index] + " index=" + index);\r
+ int fi = ClusterTraits.completeReferenceGetForeignIndex(table[index]);\r
+ int ri = ClusterTraits.completeReferenceGetResourceIndex(table[index]);\r
+ if (0 != fi) {\r
+ cluster.checkForeingIndex(fi);\r
+ if (ri < 1 || ri > ClusterTraits.getMaxNumberOfResources())\r
+ throw new ValidationException("Illegal CompleteTable foreign entry. Entry=" + table[index] + " index=" + index);\r
+ } /*else if (ri < 1 || ri > cluster.getNumberOfResources(-1))\r
+ throw new ValidationException("Illegal CompleteTable local entry. Entry=" + table[index] + " index=" + index);*/\r
+ }\r
+\r
+ private TIntHashSet checkIndexSet = null;\r
+ public final 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]; p<e; p++) {\r
+ if (IntHashTrait.isFull(table[p])) \r
+ checkEntry(cluster, table, p);\r
+ }\r
+ } else {\r
+ final int size = -table[cap];\r
+ assert(size > 0);\r
+ checkIndexSet.add(p - ps);\r
+ boolean free = false;\r
+ for (int e = p + size; p<e; p++) {\r
+ if (free)\r
+ assert(table[p] == 0);\r
+ else if (table[p] == 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 checkCompleteSetIndex(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 object set index=" + i);\r
+ }\r
+ final 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];\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;\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("complete 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("complete 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, ClusterSupport support, Modifier modifier) throws DatabaseException {\r
+ return foreachComplete(setIndex, (ObjectProcedure<Context>)procedure, context, support, modifier);\r
+ }\r
+}\r
+class ForeachPredicate<Context>\r
+implements ClusterI.ObjectProcedure<Context> {\r
+ private TIntHashSet completeTypes = new TIntHashSet();\r
+ private ClusterI.PredicateProcedure<Context> procedure; \r
+ public ForeachPredicate(ClusterI.PredicateProcedure<Context>\r
+ procedure, ClusterSupport support, Modifier modifier) {\r
+ this.procedure = procedure;\r
+ }\r
+ @Override\r
+ public boolean execute(Context context, int completeRef) {\r
+ ClusterI.CompleteTypeEnum completeType = ClusterTraits.completeReferenceGetType(completeRef);\r
+ if (!completeTypes.contains(completeType.getValue())) {\r
+ completeTypes.add(completeType.getValue());\r
+ try {\r
+ int pKey = ClusterTraitsBase.getCompleteTypeResourceKeyFromEnum(completeType);\r
+ if (procedure.execute(context, pKey, 0))\r
+ return true; // loop broken by procedure\r
+ } catch (DatabaseException e) {\r
+ e.printStackTrace();\r
+ return false;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+ \r
+}\r
+class ForeachObject<Context>\r
+implements ClusterI.ObjectProcedure<Context> {\r
+ private ClusterI.ObjectProcedure<Context> procedure; \r
+ private Modifier modifier;\r
+ private ClusterI.CompleteTypeEnum completeType;\r
+ public ForeachObject(ClusterI.ObjectProcedure<Context>\r
+ procedure, ClusterSupport support, Modifier modifier, ClusterI.CompleteTypeEnum completeType) {\r
+ this.procedure = procedure;\r
+ this.modifier = modifier;\r
+ this.completeType = completeType;\r
+ }\r
+ @Override\r
+ public boolean execute(Context context, int completeRef) throws DatabaseException {\r
+ ClusterI.CompleteTypeEnum completeType2 = ClusterTraits.completeReferenceGetType(completeRef);\r
+ if (completeType == completeType2) {\r
+ int clusterIndex = ClusterTraits.completeReferenceGetForeignIndex(completeRef);\r
+ int resourceIndex = ClusterTraits.completeReferenceGetResourceIndex(completeRef);\r
+ if (0 == clusterIndex) {\r
+ int externalRef;\r
+ if (null == modifier)\r
+ externalRef = resourceIndex;\r
+ else\r
+ externalRef = modifier.execute(resourceIndex);\r
+ return procedure.execute(context, externalRef);\r
+ } else {\r
+ try {\r
+ int externalRef = ClusterTraits.createForeignReference(clusterIndex, resourceIndex);\r
+ if (null != modifier)\r
+ externalRef = modifier.execute(externalRef);\r
+ return procedure.execute(context, externalRef);\r
+ } catch (DatabaseException e) {\r
+ e.printStackTrace();\r
+ return false; // continue looping\r
+ }\r
+ }\r
+ }\r
+ return false; // continue looping\r
+ }\r
+ \r
+}
\ No newline at end of file