da3b7903e7e270f2f1f6f5885ba12a3900700690
[simantics/platform.git] / bundles / org.simantics.db.procore / src / org / simantics / db / procore / cluster / ObjectTable.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.db.procore.cluster;
13
14 import org.simantics.db.Resource;
15 import org.simantics.db.exception.DatabaseException;
16 import org.simantics.db.exception.ValidationException;
17 import org.simantics.db.impl.ClusterBase;
18 import org.simantics.db.impl.ClusterI;
19 import org.simantics.db.impl.ClusterI.ObjectProcedure;
20 import org.simantics.db.impl.ClusterI.Procedure;
21 import org.simantics.db.impl.ClusterSupport;
22 import org.simantics.db.impl.Modifier;
23 import org.simantics.db.impl.ResourceImpl;
24 import org.simantics.db.impl.Table;
25 import org.simantics.db.impl.TableFactory;
26 import org.simantics.db.impl.TableIntAllocatorAdapter;
27 import org.simantics.db.impl.TableSizeListener;
28 import org.simantics.db.impl.graph.ReadGraphImpl;
29 import org.simantics.db.procedure.AsyncContextMultiProcedure;
30 import org.simantics.db.procedure.AsyncMultiProcedure;
31 import org.simantics.db.procore.cluster.TableIntArraySet.Ints;
32
33 import gnu.trove.map.hash.TIntIntHashMap;
34 import gnu.trove.procedure.TIntIntProcedure;
35 import gnu.trove.set.hash.TIntHashSet;
36
37 public final class ObjectTable extends Table<int[]> {
38
39         final TableIntAllocatorAdapter allocator;
40         
41     public ObjectTable(TableSizeListener sizeListener, int[] header, int headerBase) {
42         super(TableFactory.getIntFactory(), sizeListener, header, headerBase);
43         allocator = new TableIntAllocatorAdapter(this);
44     }
45
46     public ObjectTable(TableSizeListener sizeListener, int[] header, int headerBase, int[] ints) {
47         super(TableFactory.getIntFactory(), sizeListener, header, headerBase, ints);
48         allocator = new TableIntAllocatorAdapter(this);
49     }
50
51     final int createObjectSet(int o1, int o2) throws DatabaseException {
52         if (0 == o1 || o1 == o2)
53             throw new DatabaseException("Illegal argument to createObejctSet");
54         int[] obs = new int[2];
55         obs[0] = o1;
56         obs[1] = o2;
57         int hashBase = TableIntArraySet.create(obs, allocator);
58         return convertRealIndexToTableIndex(hashBase);
59     }
60
61     final void deleteObjectSet(int objectIndex) throws DatabaseException {
62         int hashBase = checkIndexAndGetRealIndex(objectIndex, 0);
63         if (TableIntArraySet.isArraySet(getTable(), hashBase)) {
64             int capacity = TableIntArraySet.getAllocatedSize(getTable(), hashBase);
65             int elementIndex = objectIndex - TableIntArraySet.HeaderSize;
66             deleteOldElement(elementIndex, capacity);
67         } else {
68             int capacity = TableIntSet.getAllocatedSize(getTable(), hashBase);
69             int elementIndex = objectIndex - TableIntSet.HeaderSize;
70             deleteOldElement(elementIndex, capacity);
71         }
72     }
73
74     public final int getObjectSetSize(int objectIndex) {
75         int hashBase = checkIndexAndGetRealIndex(objectIndex, 0);
76         if (TableIntArraySet.isArraySet(getTable(), hashBase))
77             return TableIntArraySet.getSize(getTable(), hashBase);
78         else
79             return TableIntSet.getSize(getTable(), hashBase);
80     }
81
82     /**
83      * @param objectIndex
84      * @param oResourceIndex
85      * @return zero if object already in the set else object index of the set
86      */
87     final int addObject(int objectIndex, int oResourceIndex) throws DatabaseException {
88         int hashBase = checkIndexAndGetRealIndex(objectIndex, 0);
89         int newHashBase;
90         if (TableIntArraySet.isArraySet(getTable(), hashBase)) {
91             if (TableIntArraySet.getSize(getTable(), hashBase) < 5)
92                 newHashBase = TableIntArraySet.addInt(getTable(), hashBase, oResourceIndex, allocator);
93             else {
94                 Ints ints = TableIntArraySet.getIntsIfValueNotFound(getTable(), hashBase, oResourceIndex);
95                 if (ints.found)
96                     return 0; // old object, not modified
97                 this.deleteObjectSet(objectIndex);
98                 newHashBase = TableIntSet.create(ints.ints, allocator);
99                 assert(0 != newHashBase);
100             }
101         } else
102             newHashBase = TableIntSet.addInt(getTable(), hashBase, oResourceIndex, allocator);
103         if (0 == newHashBase)
104             return 0; // old object, not modified
105         int ni = convertRealIndexToTableIndex(newHashBase);
106         return ni;
107     }
108
109     /**
110      * @param objectIndex
111      * @param oResourceIndex
112      * @return number of objects after removal.
113      */
114     final int removeObject(int objectIndex, int oResourceIndex) throws DatabaseException {
115         if (ClusterTraits.statementIndexIsDirect(objectIndex)) {
116             int pRef = objectIndex;
117             if (oResourceIndex == pRef) {
118                 return 0;
119             } else
120                 return 1;
121         } else
122             objectIndex = ClusterTraits.statementIndexGet(objectIndex);
123         int hashBase = checkIndexAndGetRealIndex(objectIndex, 0);
124         int[] table = getTable();
125         if (TableIntArraySet.isArraySet(table, hashBase))
126             return TableIntArraySet.removeInt(table, hashBase, oResourceIndex);
127         else {
128             TableIntSet.removeInt(table, hashBase, oResourceIndex);
129             return TableIntSet.getSize(table, hashBase);
130         }
131     }
132
133     final public int getSingleObject(final int objectIndex, final ClusterSupport support, Modifier modifier) throws DatabaseException {
134
135         if (ClusterTraits.statementIndexIsDirect(objectIndex)) {
136                 return modifier.execute(objectIndex);
137         }
138         
139         int realObjectIndex = ClusterTraits.statementIndexGet(objectIndex);
140         final int hashBase = checkIndexAndGetRealIndex(realObjectIndex, 0);
141         if (TableIntArraySet.isArraySet(getTable(), hashBase))
142             return TableIntArraySet.getSingleInt(getTable(), hashBase, modifier);
143         else
144                 return IntHash.getSingleInt(getTable(), hashBase, modifier);
145         
146     }
147
148     final public void foreachObject( ReadGraphImpl graph, final int objectIndex, 
149             final AsyncMultiProcedure<Resource> procedure, Modifier modifier) throws DatabaseException {
150         if (ClusterTraits.statementIndexIsDirect(objectIndex)) {
151                 int key = modifier.execute(objectIndex);
152             procedure.execute(graph, new ResourceImpl(graph.getResourceSupport(), key));
153                 procedure.finished(graph);
154 //              graph.dec();
155             return;
156         }
157         int realObjectIndex = ClusterTraits.statementIndexGet(objectIndex);
158         final int hashBase = checkIndexAndGetRealIndex(realObjectIndex, 0);
159         if (TableIntArraySet.isArraySet(getTable(), hashBase))
160             TableIntArraySet.foreachInt(getTable(), hashBase, graph, procedure, modifier);
161         else
162                 IntHash.foreachInt(graph, table, hashBase, procedure, modifier);
163     }
164
165     final public <C> void foreachObject( ReadGraphImpl graph, final int objectIndex, C context, 
166             final AsyncContextMultiProcedure<C, Resource> procedure, Modifier modifier) throws DatabaseException {
167         if (ClusterTraits.statementIndexIsDirect(objectIndex)) {
168                 int key = modifier.execute(objectIndex);
169             procedure.execute(graph, context, new ResourceImpl(graph.getResourceSupport(), key));
170                 procedure.finished(graph, context);
171 //              graph.dec();
172             return;
173         }
174         int realObjectIndex = ClusterTraits.statementIndexGet(objectIndex);
175         final int hashBase = checkIndexAndGetRealIndex(realObjectIndex, 0);
176         if (TableIntArraySet.isArraySet(getTable(), hashBase))
177             TableIntArraySet.foreachInt(getTable(), hashBase, graph, context, procedure, modifier);
178         else
179                 IntHash.foreachInt(graph, table, hashBase, context, procedure, modifier);
180     }
181
182     final public <Context> boolean foreachObject(final int objectIndex,
183             final ClusterI.ObjectProcedure<Context> procedure, final Context context, final ClusterSupport support,
184             final Modifier modifier) throws DatabaseException {
185         if (ClusterTraits.statementIndexIsDirect(objectIndex)) {
186             int pRef = objectIndex;
187             int key;
188             if (null == modifier)
189                 key = pRef;
190             else
191                 key = modifier.execute(pRef);
192             if (procedure.execute(context, key))
193                 return true; // loop broken by procedure
194             return false; // loop finished
195         }
196         int realObjectIndex = ClusterTraits.statementIndexGet(objectIndex);
197         final int hashBase = checkIndexAndGetRealIndex(realObjectIndex, 0);
198         boolean ret;
199         if (TableIntArraySet.isArraySet(getTable(), hashBase))
200             ret = TableIntArraySet.foreachInt(getTable(), hashBase, procedure, context, modifier);
201         else
202             ret = TableIntSet.foreachInt(getTable(), hashBase, procedure, context, modifier);
203         return ret;
204     }
205
206     private void checkEntry(ClusterBase cluster, int[] table, int index)
207     throws DatabaseException {
208         if (!ClusterTraits.statementIndexIsDirect(table[index]))
209                 throw new ValidationException("Illegal ObjectTable entry. Entry=" + table[index] + " index=" + index);
210         int dr = table[index];
211         cluster.checkDirectReference(dr);
212     }
213     private TIntHashSet checkIndexSet = null;
214     public final void check(ClusterBase cluster)
215     throws DatabaseException {
216         if (null == checkIndexSet)
217                 checkIndexSet = new TIntHashSet();
218         else
219                 checkIndexSet.clear();
220         int count = 0;
221         int[] table = getTable();
222         int ps = getHeader().getOffset() + ZERO_SHIFT;
223         int pe = ps + getTableSize();
224         for (int p = ps; p < pe;) {
225             int cap = p++;
226             if (table[cap] >= 0) {
227                 int use = p++;
228                 int fre = p++;
229                 int max = p++;
230                 assert(table[cap] >= table[use] + table[fre]);
231                 assert(table[max] == table[cap] >> 1);
232                 assert(table[max]+1 >= table[use]);
233                 checkIndexSet.add(p - ps);
234                 for (int e = p + table[cap]; p<e; p++) {
235                     if (IntHashTrait.isFull(table[p])) 
236                         checkEntry(cluster, table, p);
237                 }
238             } else {
239                 final int size = -table[cap];
240                 assert(size > 0);
241                 checkIndexSet.add(p - ps);
242                 boolean free = false;
243                 for (int e = p + size; p<e; p++) {
244                     if (free)
245                         assert(table[p] == 0);
246                     else if (table[p] == 0)
247                         free = true;
248                     else
249                         checkEntry(cluster, table, p);
250                 }
251             }
252             count++;
253         }
254         assert(getHeader().getCount() <= count); // deleted objects are not recognized
255     }
256
257     public final void checkObjectSetIndex(ClusterBase cluster, int i)
258     throws DatabaseException {
259         if (null == checkIndexSet)
260                 check(cluster); // builds checkIndexSet
261         if (!checkIndexSet.contains(i-ZERO_SHIFT))
262                 throw new ValidationException("Illegal object set index=" + i);
263     }
264     public final void printDebugInfo() {
265         //int count = 0;
266         int[] table = getTable();
267         int ps = getHeader().getOffset() + ZERO_SHIFT;
268         int pe = ps + getTableSize();
269         TIntIntHashMap stat = new TIntIntHashMap();
270         TIntIntHashMap stat2 = new TIntIntHashMap();
271         for (int p = ps; p < pe;) {
272             int cap = p++;
273             if (table[cap] >= 0) {
274                 int use = p++;
275                 int fre = p++;
276                 int max = p++;
277                 assert(table[cap] >= table[use] + table[fre]);
278                 assert(table[max] == table[cap] >> 1);
279                 p += table[cap];
280                 int val = stat.get(table[use]) + 1;
281                 stat.put(table[use], val);
282             } else {
283                 final int size = -table[cap];
284                 int val = stat2.get(size) + 1;
285                 stat2.put(size, val);
286                 p += size;
287             }
288             //count++;
289         }
290         // assert(getHeader().getCount() == count);
291         stat.forEachEntry(new TIntIntProcedure() {
292             @Override
293             public boolean execute(int a, int b) {
294                 System.out.println("object set capacity " + a + " instance count " + b);
295                 return true;
296             }
297         });
298         stat2.forEachEntry(new TIntIntProcedure() {
299             @Override
300             public boolean execute(int a, int b) {
301                 System.out.println("object array set capacity " + a + " instance count " + b);
302                 return true;
303             }
304         });
305     }
306
307     @Override
308     public <Context> boolean foreach(int setIndex, Procedure procedure, Context context,
309             ClusterSupport support, Modifier modifier) throws DatabaseException {
310         return foreachObject(setIndex, (ObjectProcedure<Context>)procedure, context, support, modifier);
311     }
312 }