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.procore.cluster;
\r
14 import gnu.trove.map.hash.TIntIntHashMap;
\r
15 import gnu.trove.procedure.TIntIntProcedure;
\r
16 import gnu.trove.set.hash.TIntHashSet;
\r
18 import org.simantics.db.exception.DatabaseException;
\r
19 import org.simantics.db.exception.ValidationException;
\r
20 import org.simantics.db.impl.ClusterBase;
\r
21 import org.simantics.db.impl.ClusterI;
\r
22 import org.simantics.db.impl.ClusterI.ObjectProcedure;
\r
23 import org.simantics.db.impl.ClusterI.PredicateProcedure;
\r
24 import org.simantics.db.impl.ClusterI.Procedure;
\r
25 import org.simantics.db.impl.ClusterSupport;
\r
26 import org.simantics.db.impl.Modifier;
\r
27 import org.simantics.db.impl.Table;
\r
28 import org.simantics.db.impl.TableFactory;
\r
29 import org.simantics.db.impl.TableIntAllocatorAdapter;
\r
30 import org.simantics.db.impl.TableSizeListener;
\r
31 import org.simantics.db.procore.cluster.TableIntArraySet2.Tables;
\r
33 public final class PredicateTable extends Table<int[]> {
\r
35 final TableIntAllocatorAdapter allocator;
\r
37 public PredicateTable(TableSizeListener sizeListener, int[] header, int headerBase) {
\r
38 super(TableFactory.getIntFactory(), sizeListener, header, headerBase);
\r
39 allocator = new TableIntAllocatorAdapter(this);
\r
41 public PredicateTable(TableSizeListener sizeListener, int[] header, int headerBase, int[] ints) {
\r
42 super(TableFactory.getIntFactory(), sizeListener, header, headerBase, ints);
\r
43 allocator = new TableIntAllocatorAdapter(this);
\r
45 int createPredicateSet(int[] ps, int[] os)
\r
46 throws DatabaseException {
\r
47 int hashBase = TableIntArraySet2.create(ps, os, allocator);
\r
48 return convertRealIndexToTableIndex(hashBase);
\r
50 void deletePredicateSet(int predicateIndex) {
\r
51 int hashBase = checkIndexAndGetRealIndex(predicateIndex, 0);
\r
52 if (TableIntArraySet2.isArraySet(getTable(), hashBase)) {
\r
53 int capacity = TableIntArraySet2.getAllocatedSize(getTable(), hashBase);
\r
54 int elementIndex = predicateIndex - TableIntArraySet2.HeaderSize;
\r
55 deleteOldElement(elementIndex, capacity);
\r
57 int capacity = TableIntSet2.getAllocatedSize(getTable(), hashBase);
\r
58 int elementIndex = predicateIndex - TableIntSet2.HeaderSize;
\r
59 deleteOldElement(elementIndex, capacity);
\r
62 public int getPredicateSetSize(int predicateIndex) {
\r
63 int hashBase = checkIndexAndGetRealIndex(predicateIndex, 0);
\r
64 if (TableIntArraySet2.isArraySet(getTable(), hashBase))
\r
65 return TableIntArraySet2.getSize(getTable(), hashBase);
\r
67 return TableIntSet2.getSize(getTable(), hashBase);
\r
69 public int getObjectIndex(int predicateIndex, int pRef) {
\r
70 int hashBase = checkIndexAndGetRealIndex(predicateIndex, 0);
\r
71 if (TableIntArraySet2.isArraySet(table, hashBase))
\r
72 return TableIntArraySet2.get(table, hashBase, pRef);
\r
74 return TableIntSet2.get(table, hashBase, pRef);
\r
77 private int addPredicateArray(int predicateIndex, int hashBase, int pReference, int oReference, ObjectTable ot)
\r
78 throws DatabaseException {
\r
80 int objectIndex = TableIntArraySet2.get(getTable(), hashBase, pReference);
\r
81 if (0 == objectIndex) {
\r
82 newHashBase = TableIntArraySet2.addInt(getTable(), hashBase, pReference, oReference, allocator);
\r
83 } else if (ClusterTraits.statementIndexIsDirect(objectIndex)) {
\r
84 int oRef = objectIndex;
\r
85 if (oRef == oReference) {
\r
86 return 0; // old direct object
\r
88 objectIndex = ot.createObjectSet(oRef, oReference);
\r
89 assert(0 != objectIndex);
\r
90 int newObjectIndex = ClusterTraits.statementIndexMake(objectIndex);
\r
91 newHashBase = TableIntArraySet2.addInt(getTable(), hashBase, pReference, newObjectIndex, allocator);
\r
93 int newObjectIndex = ot.addObject(ClusterTraits.statementIndexGet(objectIndex), oReference);
\r
94 if (0 == newObjectIndex)
\r
95 return 0; // old indirect object
\r
96 newObjectIndex = ClusterTraits.statementIndexMake(newObjectIndex);
\r
97 newHashBase = TableIntArraySet2.addInt(getTable(), hashBase, pReference, newObjectIndex, allocator);
\r
98 if (newHashBase == 0)
\r
101 int TABLE_SIZE = TableIntArraySet2.getSize(getTable(), newHashBase);
\r
102 if (TABLE_SIZE > 5)
\r
103 return convertToPredicateSet(predicateIndex, newHashBase);
\r
104 return newHashBase;
\r
106 private int convertToPredicateSet(int predicateIndex, int hashBase) {
\r
107 Tables tables = TableIntArraySet2.getInts(getTable(), hashBase);
\r
108 this.deletePredicateSet(predicateIndex);
\r
109 int newHashBase = TableIntSet2.create(tables.keys, tables.vals, allocator);
\r
110 assert(0 != newHashBase);
\r
111 return newHashBase;
\r
113 private int addPredicateSet(int hashBase, int pReference, int oReference, ObjectTable ot)
\r
114 throws DatabaseException {
\r
115 int objectIndex = TableIntSet2.get(getTable(), hashBase, pReference);
\r
117 if (0 == objectIndex) {
\r
118 newHashBase = TableIntSet2.addInt(getTable(), hashBase, pReference, oReference, allocator);
\r
119 } else if (ClusterTraits.statementIndexIsDirect(objectIndex)) {
\r
120 int oRef = objectIndex;
\r
121 if (oRef == oReference) {
\r
122 return 0; // old direct object
\r
124 objectIndex = ot.createObjectSet(oRef, oReference);
\r
125 assert(0 != objectIndex);
\r
126 int newObjectIndex = ClusterTraits.statementIndexMake(objectIndex);
\r
127 newHashBase = TableIntSet2.addInt(getTable(), hashBase, pReference, newObjectIndex, allocator);
\r
128 assert(0 != newHashBase);
\r
130 int newObjectIndex = ot.addObject(ClusterTraits.statementIndexGet(objectIndex), oReference);
\r
131 if (0 == newObjectIndex)
\r
132 return 0; // old indirect object
\r
133 int stmIndex = ClusterTraits.statementIndexMake(newObjectIndex);
\r
134 newHashBase = TableIntSet2.addInt(getTable(), hashBase, pReference, stmIndex, allocator);
\r
135 if (newHashBase == 0)
\r
136 return hashBase; // new object added to old predicate (set)
\r
138 return newHashBase;
\r
141 * @param predicateIndex
\r
142 * @param pReference
\r
143 * @return zero if element was not added or predicate index.
\r
144 * Predicate index will change if new space is allocated.
\r
146 public int addPredicate(int predicateIndex, int pReference, int oReference, ObjectTable ot)
\r
147 throws DatabaseException {
\r
148 int hashBase = checkIndexAndGetRealIndex(predicateIndex, 0);
\r
150 if (TableIntArraySet2.isArraySet(getTable(), hashBase))
\r
151 newHashBase = addPredicateArray(predicateIndex, hashBase, pReference, oReference, ot);
\r
153 newHashBase = addPredicateSet(hashBase, pReference, oReference, ot);
\r
154 if (0 == newHashBase)
\r
155 return 0; // not modified
\r
156 return convertRealIndexToTableIndex(newHashBase);
\r
158 public enum Status {
\r
164 * @param predicateIndex
\r
165 * @param oResourceIndex
\r
166 * @return null if nothing was removed. Status if either object or both
\r
167 * object and predicate were removed.
\r
169 public Status removePredicate(int predicateIndex, int pReference, int oReference, ObjectTable ot)
\r
170 throws DatabaseException {
\r
171 int hashBase = checkIndexAndGetRealIndex(predicateIndex, 0);
\r
172 int[] table = getTable();
\r
173 if (TableIntArraySet2.isArraySet(table, hashBase)) {
\r
174 int objectIndex = TableIntArraySet2.get(table, hashBase, pReference);
\r
175 if (0 == objectIndex) {
\r
176 return Status.NothingRemoved;
\r
177 } else if (ClusterTraits.statementIndexIsDirect(objectIndex)) {
\r
178 int oRef = objectIndex;
\r
179 if (oRef != oReference)
\r
180 return Status.NothingRemoved;
\r
181 if (TableIntArraySet2.removeInt(table, hashBase, pReference))
\r
182 return Status.PredicateRemoved;
\r
184 throw new DatabaseException("Internal error during remove.");
\r
186 int oIndex = ClusterTraits.statementIndexGet(objectIndex);
\r
187 int nO = ot.getObjectSetSize(oIndex);
\r
189 throw new DatabaseException("Illegal object set size="+nO);
\r
190 int nObject = ot.removeObject(objectIndex, oReference);
\r
191 if (nObject == 0) {
\r
192 ot.deleteObjectSet(oIndex);
\r
193 if (TableIntArraySet2.removeInt(table, hashBase, pReference))
\r
194 return Status.PredicateRemoved;
\r
196 throw new DatabaseException("Internal error during remove (2).");
\r
197 } else if (nO == nObject)
\r
198 return Status.NothingRemoved;
\r
200 return Status.ObjectRemoved;
\r
203 int objectIndex = TableIntSet2.get(table, hashBase, pReference);
\r
204 if (0 == objectIndex) {
\r
205 return Status.NothingRemoved;
\r
206 } else if (ClusterTraits.statementIndexIsDirect(objectIndex)) {
\r
207 int oRef = objectIndex;
\r
208 if (oRef != oReference)
\r
209 return Status.NothingRemoved;
\r
210 if (TableIntSet2.removeInt(table, hashBase, pReference))
\r
211 return Status.PredicateRemoved;
\r
213 throw new DatabaseException("Internal error during remove (3).");
\r
215 int oIndex = ClusterTraits.statementIndexGet(objectIndex);
\r
216 int nO = ot.getObjectSetSize(oIndex);
\r
218 throw new DatabaseException("Illegal object set size="+nO);
\r
219 int nObject = ot.removeObject(objectIndex, oReference);
\r
220 if (nObject == 0) {
\r
221 ot.deleteObjectSet(oIndex);
\r
222 if (TableIntSet2.removeInt(table, hashBase, pReference))
\r
223 return Status.PredicateRemoved;
\r
225 throw new DatabaseException("Internal error during remove (4).");
\r
226 } else if (nO == nObject)
\r
227 return Status.NothingRemoved;
\r
229 return Status.ObjectRemoved;
\r
233 public <Context> boolean foreachPredicate(int predicateIndex
\r
234 , ClusterI.PredicateProcedure<Context> procedure
\r
235 , Context context, ClusterSupport support, Modifier modifier)
\r
236 throws DatabaseException {
\r
237 final int hashBase = checkIndexAndGetRealIndex(predicateIndex, 0);
\r
239 if (TableIntArraySet2.isArraySet(getTable(), hashBase))
\r
240 ret = TableIntArraySet2.foreachInt(getTable(), hashBase, procedure, context, modifier);
\r
242 ret = TableIntSet2.foreachInt(getTable(), hashBase, procedure, context, modifier);
\r
245 private void checkEntry(ClusterBase cluster, int[] table, int index)
\r
246 throws DatabaseException {
\r
247 assert(ClusterTraits.statementIndexIsDirect(table[index]));
\r
248 int dr = table[index];
\r
249 cluster.checkDirectReference(dr);
\r
250 assert(table[index+1] != 0);
\r
251 if (ClusterTraits.statementIndexIsDirect(table[index+1])) {
\r
252 cluster.checkDirectReference(table[index+1]);
\r
254 cluster.checkObjectSetReference(table[index+1]);
\r
257 private TIntHashSet checkIndexSet = null;
\r
258 public void check(ClusterBase cluster)
\r
259 throws DatabaseException {
\r
260 if (null == checkIndexSet)
\r
261 checkIndexSet = new TIntHashSet();
\r
263 checkIndexSet.clear();
\r
265 int[] table = getTable();
\r
266 int ps = getHeader().getOffset() + ZERO_SHIFT;
\r
267 int pe = ps + getTableSize();
\r
268 for (int p=ps; p<pe;) {
\r
270 if (table[cap] >= 0) {
\r
274 assert(table[cap] >= table[use] + table[fre]);
\r
275 assert(table[max] == table[cap] >> 1);
\r
276 assert(table[max]+1 >= table[use]);
\r
277 checkIndexSet.add(p - ps);
\r
278 for (int e = p + table[cap]*2; p<e; p+=2) {
\r
279 if (!IntHashTrait.isFull(table[p]))
\r
280 assert(table[p+1]==0);
\r
282 checkEntry(cluster, table, p);
\r
285 final int size = -table[cap];
\r
287 boolean free = false;
\r
288 checkIndexSet.add(p - ps);
\r
289 for (int e = p + size*2; p<e; p+=2) {
\r
291 assert(table[p] == 0);
\r
292 assert(table[p+1] == 0);
\r
294 else if (table[p] == 0) {
\r
295 assert(table[p+1] == 0);
\r
298 checkEntry(cluster, table, p);
\r
303 assert(getHeader().getCount() <= count); // deleted objects are not recognized
\r
305 public final void checkPredicateSetIndex(ClusterBase cluster, int i)
\r
306 throws DatabaseException {
\r
307 if (null == checkIndexSet)
\r
308 check(cluster); // builds checkIndexSet
\r
309 if (!checkIndexSet.contains(i-ZERO_SHIFT))
\r
310 throw new ValidationException("Illegal predicate set index=" + i);
\r
312 public void printDebugInfo() {
\r
314 int[] table = getTable();
\r
315 int ps = getHeader().getOffset() + ZERO_SHIFT;
\r
316 int pe = ps + getTableSize();
\r
317 TIntIntHashMap stat = new TIntIntHashMap();
\r
318 TIntIntHashMap stat2 = new TIntIntHashMap();
\r
319 for (int p=ps; p<pe;) {
\r
321 if (table[cap] >= 0) {
\r
325 assert(table[cap] >= table[use] + table[fre]);
\r
326 assert(table[max] == table[cap] >> 1);
\r
328 int val = stat.get(table[use]) + 1;
\r
329 stat.put(table[use], val);
\r
331 final int size = -table[cap];
\r
332 int val = stat2.get(size) + 1;
\r
333 stat2.put(size, val);
\r
338 //assert(getHeader().getCount() == count);
\r
339 stat.forEachEntry(new TIntIntProcedure() {
\r
341 public boolean execute(int a, int b) {
\r
342 System.out.println("predicate set capacity " + a + " instance count " + b);
\r
346 stat2.forEachEntry(new TIntIntProcedure() {
\r
348 public boolean execute(int a, int b) {
\r
349 System.out.println("predicate array set capacity " + a + " instance count " + b);
\r
355 public <Context> boolean foreach(int setIndex, Procedure procedure, Context context,
\r
356 ClusterSupport support, Modifier modifier) throws DatabaseException {
\r
357 return foreachPredicate(setIndex, (PredicateProcedure<Context>)procedure, context, support, modifier);
\r