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 org.simantics.db.Resource;
\r
15 import org.simantics.db.exception.DatabaseException;
\r
16 import org.simantics.db.exception.ValidationException;
\r
17 import org.simantics.db.impl.ClusterBase;
\r
18 import org.simantics.db.impl.ClusterI;
\r
19 import org.simantics.db.impl.ClusterI.ObjectProcedure;
\r
20 import org.simantics.db.impl.ClusterI.Procedure;
\r
21 import org.simantics.db.impl.ClusterSupport;
\r
22 import org.simantics.db.impl.Modifier;
\r
23 import org.simantics.db.impl.ResourceImpl;
\r
24 import org.simantics.db.impl.Table;
\r
25 import org.simantics.db.impl.TableFactory;
\r
26 import org.simantics.db.impl.TableIntAllocatorAdapter;
\r
27 import org.simantics.db.impl.TableSizeListener;
\r
28 import org.simantics.db.impl.graph.ReadGraphImpl;
\r
29 import org.simantics.db.procedure.AsyncContextMultiProcedure;
\r
30 import org.simantics.db.procedure.AsyncMultiProcedure;
\r
31 import org.simantics.db.procore.cluster.TableIntArraySet.Ints;
\r
33 import gnu.trove.map.hash.TIntIntHashMap;
\r
34 import gnu.trove.procedure.TIntIntProcedure;
\r
35 import gnu.trove.set.hash.TIntHashSet;
\r
37 public final class ObjectTable extends Table<int[]> {
\r
39 final TableIntAllocatorAdapter allocator;
\r
41 public ObjectTable(TableSizeListener sizeListener, int[] header, int headerBase) {
\r
42 super(TableFactory.getIntFactory(), sizeListener, header, headerBase);
\r
43 allocator = new TableIntAllocatorAdapter(this);
\r
46 public ObjectTable(TableSizeListener sizeListener, int[] header, int headerBase, int[] ints) {
\r
47 super(TableFactory.getIntFactory(), sizeListener, header, headerBase, ints);
\r
48 allocator = new TableIntAllocatorAdapter(this);
\r
51 final int createObjectSet(int o1, int o2) throws DatabaseException {
\r
52 if (0 == o1 || o1 == o2)
\r
53 throw new DatabaseException("Illegal argument to createObejctSet");
\r
54 int[] obs = new int[2];
\r
57 int hashBase = TableIntArraySet.create(obs, allocator);
\r
58 return convertRealIndexToTableIndex(hashBase);
\r
61 final void deleteObjectSet(int objectIndex) throws DatabaseException {
\r
62 int hashBase = checkIndexAndGetRealIndex(objectIndex, 0);
\r
63 if (TableIntArraySet.isArraySet(getTable(), hashBase)) {
\r
64 int capacity = TableIntArraySet.getAllocatedSize(getTable(), hashBase);
\r
65 int elementIndex = objectIndex - TableIntArraySet.HeaderSize;
\r
66 deleteOldElement(elementIndex, capacity);
\r
68 int capacity = TableIntSet.getAllocatedSize(getTable(), hashBase);
\r
69 int elementIndex = objectIndex - TableIntSet.HeaderSize;
\r
70 deleteOldElement(elementIndex, capacity);
\r
74 public final int getObjectSetSize(int objectIndex) {
\r
75 int hashBase = checkIndexAndGetRealIndex(objectIndex, 0);
\r
76 if (TableIntArraySet.isArraySet(getTable(), hashBase))
\r
77 return TableIntArraySet.getSize(getTable(), hashBase);
\r
79 return TableIntSet.getSize(getTable(), hashBase);
\r
83 * @param objectIndex
\r
84 * @param oResourceIndex
\r
85 * @return zero if object already in the set else object index of the set
\r
87 final int addObject(int objectIndex, int oResourceIndex) throws DatabaseException {
\r
88 int hashBase = checkIndexAndGetRealIndex(objectIndex, 0);
\r
90 if (TableIntArraySet.isArraySet(getTable(), hashBase)) {
\r
91 if (TableIntArraySet.getSize(getTable(), hashBase) < 5)
\r
92 newHashBase = TableIntArraySet.addInt(getTable(), hashBase, oResourceIndex, allocator);
\r
94 Ints ints = TableIntArraySet.getIntsIfValueNotFound(getTable(), hashBase, oResourceIndex);
\r
96 return 0; // old object, not modified
\r
97 this.deleteObjectSet(objectIndex);
\r
98 newHashBase = TableIntSet.create(ints.ints, allocator);
\r
99 assert(0 != newHashBase);
\r
102 newHashBase = TableIntSet.addInt(getTable(), hashBase, oResourceIndex, allocator);
\r
103 if (0 == newHashBase)
\r
104 return 0; // old object, not modified
\r
105 int ni = convertRealIndexToTableIndex(newHashBase);
\r
110 * @param objectIndex
\r
111 * @param oResourceIndex
\r
112 * @return number of objects after removal.
\r
114 final int removeObject(int objectIndex, int oResourceIndex) throws DatabaseException {
\r
115 if (ClusterTraits.statementIndexIsDirect(objectIndex)) {
\r
116 int pRef = objectIndex;
\r
117 if (oResourceIndex == pRef) {
\r
122 objectIndex = ClusterTraits.statementIndexGet(objectIndex);
\r
123 int hashBase = checkIndexAndGetRealIndex(objectIndex, 0);
\r
124 int[] table = getTable();
\r
125 if (TableIntArraySet.isArraySet(table, hashBase))
\r
126 return TableIntArraySet.removeInt(table, hashBase, oResourceIndex);
\r
128 TableIntSet.removeInt(table, hashBase, oResourceIndex);
\r
129 return TableIntSet.getSize(table, hashBase);
\r
133 final public int getSingleObject(final int objectIndex, final ClusterSupport support, Modifier modifier) throws DatabaseException {
\r
135 if (ClusterTraits.statementIndexIsDirect(objectIndex)) {
\r
136 return modifier.execute(objectIndex);
\r
139 int realObjectIndex = ClusterTraits.statementIndexGet(objectIndex);
\r
140 final int hashBase = checkIndexAndGetRealIndex(realObjectIndex, 0);
\r
141 if (TableIntArraySet.isArraySet(getTable(), hashBase))
\r
142 return TableIntArraySet.getSingleInt(getTable(), hashBase, modifier);
\r
144 return IntHash.getSingleInt(getTable(), hashBase, modifier);
\r
148 final public void foreachObject( ReadGraphImpl graph, final int objectIndex,
\r
149 final AsyncMultiProcedure<Resource> procedure, Modifier modifier) throws DatabaseException {
\r
150 if (ClusterTraits.statementIndexIsDirect(objectIndex)) {
\r
151 int key = modifier.execute(objectIndex);
\r
152 procedure.execute(graph, new ResourceImpl(graph.getResourceSupport(), key));
\r
153 procedure.finished(graph);
\r
157 int realObjectIndex = ClusterTraits.statementIndexGet(objectIndex);
\r
158 final int hashBase = checkIndexAndGetRealIndex(realObjectIndex, 0);
\r
159 if (TableIntArraySet.isArraySet(getTable(), hashBase))
\r
160 TableIntArraySet.foreachInt(getTable(), hashBase, graph, procedure, modifier);
\r
162 IntHash.foreachInt(graph, table, hashBase, procedure, modifier);
\r
165 final public <C> void foreachObject( ReadGraphImpl graph, final int objectIndex, C context,
\r
166 final AsyncContextMultiProcedure<C, Resource> procedure, Modifier modifier) throws DatabaseException {
\r
167 if (ClusterTraits.statementIndexIsDirect(objectIndex)) {
\r
168 int key = modifier.execute(objectIndex);
\r
169 procedure.execute(graph, context, new ResourceImpl(graph.getResourceSupport(), key));
\r
170 procedure.finished(graph);
\r
174 int realObjectIndex = ClusterTraits.statementIndexGet(objectIndex);
\r
175 final int hashBase = checkIndexAndGetRealIndex(realObjectIndex, 0);
\r
176 if (TableIntArraySet.isArraySet(getTable(), hashBase))
\r
177 TableIntArraySet.foreachInt(getTable(), hashBase, graph, context, procedure, modifier);
\r
179 IntHash.foreachInt(graph, table, hashBase, context, procedure, modifier);
\r
182 final public <Context> boolean foreachObject(final int objectIndex,
\r
183 final ClusterI.ObjectProcedure<Context> procedure, final Context context, final ClusterSupport support,
\r
184 final Modifier modifier) throws DatabaseException {
\r
185 if (ClusterTraits.statementIndexIsDirect(objectIndex)) {
\r
186 int pRef = objectIndex;
\r
188 if (null == modifier)
\r
191 key = modifier.execute(pRef);
\r
192 if (procedure.execute(context, key))
\r
193 return true; // loop broken by procedure
\r
194 return false; // loop finished
\r
196 int realObjectIndex = ClusterTraits.statementIndexGet(objectIndex);
\r
197 final int hashBase = checkIndexAndGetRealIndex(realObjectIndex, 0);
\r
199 if (TableIntArraySet.isArraySet(getTable(), hashBase))
\r
200 ret = TableIntArraySet.foreachInt(getTable(), hashBase, procedure, context, modifier);
\r
202 ret = TableIntSet.foreachInt(getTable(), hashBase, procedure, context, modifier);
\r
206 private void checkEntry(ClusterBase cluster, int[] table, int index)
\r
207 throws DatabaseException {
\r
208 if (!ClusterTraits.statementIndexIsDirect(table[index]))
\r
209 throw new ValidationException("Illegal ObjectTable entry. Entry=" + table[index] + " index=" + index);
\r
210 int dr = table[index];
\r
211 cluster.checkDirectReference(dr);
\r
213 private TIntHashSet checkIndexSet = null;
\r
214 public final void check(ClusterBase cluster)
\r
215 throws DatabaseException {
\r
216 if (null == checkIndexSet)
\r
217 checkIndexSet = new TIntHashSet();
\r
219 checkIndexSet.clear();
\r
221 int[] table = getTable();
\r
222 int ps = getHeader().getOffset() + ZERO_SHIFT;
\r
223 int pe = ps + getTableSize();
\r
224 for (int p = ps; p < pe;) {
\r
226 if (table[cap] >= 0) {
\r
230 assert(table[cap] >= table[use] + table[fre]);
\r
231 assert(table[max] == table[cap] >> 1);
\r
232 assert(table[max]+1 >= table[use]);
\r
233 checkIndexSet.add(p - ps);
\r
234 for (int e = p + table[cap]; p<e; p++) {
\r
235 if (IntHashTrait.isFull(table[p]))
\r
236 checkEntry(cluster, table, p);
\r
239 final int size = -table[cap];
\r
241 checkIndexSet.add(p - ps);
\r
242 boolean free = false;
\r
243 for (int e = p + size; p<e; p++) {
\r
245 assert(table[p] == 0);
\r
246 else if (table[p] == 0)
\r
249 checkEntry(cluster, table, p);
\r
254 assert(getHeader().getCount() <= count); // deleted objects are not recognized
\r
257 public final void checkObjectSetIndex(ClusterBase cluster, int i)
\r
258 throws DatabaseException {
\r
259 if (null == checkIndexSet)
\r
260 check(cluster); // builds checkIndexSet
\r
261 if (!checkIndexSet.contains(i-ZERO_SHIFT))
\r
262 throw new ValidationException("Illegal object set index=" + i);
\r
264 public final void printDebugInfo() {
\r
266 int[] table = getTable();
\r
267 int ps = getHeader().getOffset() + ZERO_SHIFT;
\r
268 int pe = ps + getTableSize();
\r
269 TIntIntHashMap stat = new TIntIntHashMap();
\r
270 TIntIntHashMap stat2 = new TIntIntHashMap();
\r
271 for (int p = ps; p < pe;) {
\r
273 if (table[cap] >= 0) {
\r
277 assert(table[cap] >= table[use] + table[fre]);
\r
278 assert(table[max] == table[cap] >> 1);
\r
280 int val = stat.get(table[use]) + 1;
\r
281 stat.put(table[use], val);
\r
283 final int size = -table[cap];
\r
284 int val = stat2.get(size) + 1;
\r
285 stat2.put(size, val);
\r
290 // assert(getHeader().getCount() == count);
\r
291 stat.forEachEntry(new TIntIntProcedure() {
\r
293 public boolean execute(int a, int b) {
\r
294 System.out.println("object set capacity " + a + " instance count " + b);
\r
298 stat2.forEachEntry(new TIntIntProcedure() {
\r
300 public boolean execute(int a, int b) {
\r
301 System.out.println("object array set capacity " + a + " instance count " + b);
\r
308 public <Context> boolean foreach(int setIndex, Procedure procedure, Context context,
\r
309 ClusterSupport support, Modifier modifier) throws DatabaseException {
\r
310 return foreachObject(setIndex, (ObjectProcedure<Context>)procedure, context, support, modifier);
\r