]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.procore/src/org/simantics/db/procore/cluster/ClusterSmall.java
Fixed ClusterTable cluster hash/importance map book-keeping problem
[simantics/platform.git] / bundles / org.simantics.db.procore / src / org / simantics / db / procore / cluster / ClusterSmall.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 java.io.ByteArrayInputStream;
15 import java.io.InputStream;
16 import java.util.function.Consumer;
17
18 import org.simantics.db.Resource;
19 import org.simantics.db.exception.DatabaseException;
20 import org.simantics.db.exception.ExternalValueException;
21 import org.simantics.db.exception.ValidationException;
22 import org.simantics.db.impl.ClusterI;
23 import org.simantics.db.impl.ClusterSupport;
24 import org.simantics.db.impl.ClusterTraitsBase;
25 import org.simantics.db.impl.ForEachObjectContextProcedure;
26 import org.simantics.db.impl.ForEachObjectProcedure;
27 import org.simantics.db.impl.ForPossibleRelatedValueContextProcedure;
28 import org.simantics.db.impl.ForPossibleRelatedValueProcedure;
29 import org.simantics.db.impl.Table;
30 import org.simantics.db.impl.TableHeader;
31 import org.simantics.db.impl.graph.ReadGraphImpl;
32 import org.simantics.db.procedure.AsyncContextMultiProcedure;
33 import org.simantics.db.procedure.AsyncMultiProcedure;
34 import org.simantics.db.service.ClusterUID;
35 import org.simantics.db.service.ResourceUID;
36
37 import fi.vtt.simantics.procore.DebugPolicy;
38 import fi.vtt.simantics.procore.internal.ClusterChange;
39 import fi.vtt.simantics.procore.internal.ClusterStream;
40 import fi.vtt.simantics.procore.internal.ClusterTable;
41 import fi.vtt.simantics.procore.internal.SessionImplSocket;
42 import gnu.trove.map.hash.TIntShortHashMap;
43 import gnu.trove.procedure.TIntProcedure;
44 import gnu.trove.set.hash.TIntHashSet;
45
46 final public class ClusterSmall extends ClusterImpl {
47     
48     private static final int TABLE_HEADER_SIZE = TableHeader.HEADER_SIZE + TableHeader.EXTRA_SIZE;
49     private static final int RESOURCE_TABLE_OFFSET = 0;
50     private static final int PREDICATE_TABLE_OFFSET = RESOURCE_TABLE_OFFSET + TABLE_HEADER_SIZE;
51     private static final int OBJECT_TABLE_OFFSET = PREDICATE_TABLE_OFFSET + TABLE_HEADER_SIZE;
52     private static final int VALUE_TABLE_OFFSET = OBJECT_TABLE_OFFSET + TABLE_HEADER_SIZE;
53     private static final int FLAT_TABLE_OFFSET = VALUE_TABLE_OFFSET + TABLE_HEADER_SIZE;
54     private static final int COMPLETE_TABLE_OFFSET = FLAT_TABLE_OFFSET + TABLE_HEADER_SIZE;
55     private static final int FOREIGN_TABLE_OFFSET = COMPLETE_TABLE_OFFSET + TABLE_HEADER_SIZE;
56     private static final int INT_HEADER_SIZE = FOREIGN_TABLE_OFFSET + TABLE_HEADER_SIZE;
57     private final int clusterBits;
58     public final ResourceTableSmall resourceTable;
59     private PredicateTable predicateTable;
60     final ObjectTable objectTable;
61     public final ValueTableSmall valueTable;
62     public final ForeignTableSmall foreignTable;
63     private final CompleteTableSmall completeTable;
64     private final ClusterMapSmall clusterMap;
65     private final int[] headerTable;
66     private final ClusterSupport clusterSupport;
67     private boolean proxy;
68     private boolean deleted = false;
69     
70     public ClusterSmall(ClusterUID clusterUID, int clusterKey, ClusterTable clusterTable, ClusterSupport support) {
71         super(clusterUID, clusterKey, support);
72         if(DebugPolicy.REPORT_CLUSTER_EVENTS)
73             new Exception(clusterUID.toString()).printStackTrace();
74         this.proxy = true;
75         this.headerTable = null;
76         this.resourceTable = null;
77         this.foreignTable = null;
78         this.predicateTable = null;
79         this.objectTable = null;
80         this.valueTable = null;
81         this.completeTable = null;
82         this.clusterMap = null;
83         this.clusterSupport = null;
84         this.clusterBits = 0;
85         this.importance = 0;
86     }
87     public ClusterSmall(ClusterUID clusterUID, int clusterKey, ClusterSupport support) {
88         super(clusterUID, clusterKey, support);
89         if(DebugPolicy.REPORT_CLUSTER_EVENTS)
90             new Exception(clusterUID.toString()).printStackTrace();
91         this.proxy = false;
92         this.headerTable = new int[INT_HEADER_SIZE];
93         this.resourceTable = new ResourceTableSmall(this, headerTable, RESOURCE_TABLE_OFFSET);
94         this.foreignTable = new ForeignTableSmall(this, headerTable, FOREIGN_TABLE_OFFSET);
95         this.predicateTable = new PredicateTable(this, headerTable, PREDICATE_TABLE_OFFSET);
96         this.objectTable = new ObjectTable(this, headerTable, OBJECT_TABLE_OFFSET);
97         this.valueTable = new ValueTableSmall(this, headerTable, VALUE_TABLE_OFFSET);
98         this.completeTable = new CompleteTableSmall(this, headerTable, COMPLETE_TABLE_OFFSET);
99         this.clusterMap = new ClusterMapSmall(this, foreignTable);
100         this.clusterSupport = support;
101         this.clusterBits = ClusterTraitsBase.getClusterBits(clusterKey);
102         this.importance = -clusterTable.timeCounter();
103     }
104     protected ClusterSmall(long[] longs, int[] ints, byte[] bytes, ClusterSupport support, int clusterKey)
105     throws DatabaseException {
106         super(checkValidity(-1, longs, ints, bytes), clusterKey, support);
107         this.proxy = false;
108         if (ints.length < INT_HEADER_SIZE)
109             throw new IllegalArgumentException("Too small integer table for cluster.");
110         this.headerTable = ints;
111         if(DebugPolicy.REPORT_CLUSTER_EVENTS) new Exception(Long.toString(clusterId)).printStackTrace();
112         this.resourceTable = new ResourceTableSmall(this, ints, RESOURCE_TABLE_OFFSET, longs);
113         this.foreignTable = new ForeignTableSmall(this, headerTable, FOREIGN_TABLE_OFFSET, longs);
114         this.predicateTable = new PredicateTable(this, ints, PREDICATE_TABLE_OFFSET, ints);
115         this.objectTable = new ObjectTable(this, ints, OBJECT_TABLE_OFFSET, ints);
116         this.valueTable = new ValueTableSmall(this, ints, VALUE_TABLE_OFFSET, bytes);
117         this.completeTable = new CompleteTableSmall(this, headerTable, COMPLETE_TABLE_OFFSET, ints);
118         this.clusterMap = new ClusterMapSmall(this, foreignTable);
119         this.clusterSupport = support;
120         this.clusterBits = ClusterTraitsBase.getClusterBits(clusterKey);
121         this.importance = clusterTable.timeCounter();
122         clusterTable.markImmutable(this, getImmutable());
123     }
124     void analyse() {
125         System.out.println("Cluster " + clusterId);
126         System.out.println("-size:" + getUsedSpace());
127         System.out.println(" -rt:" + (resourceTable.getTableCapacity() * 8 + 8));
128         System.out.println(" -ft:" + foreignTable.getTableCapacity() * 8);
129         System.out.println(" -pt:" + predicateTable.getTableCapacity() * 4);
130         System.out.println(" -ot:" + objectTable.getTableCapacity() * 4);
131         System.out.println(" -ct:" + completeTable.getTableCapacity() * 4);
132         System.out.println(" -vt:" + valueTable.getTableCapacity());
133
134         System.out.println("-resourceTable:");
135         System.out.println(" -resourceCount=" + resourceTable.getResourceCount());
136         System.out.println(" -size=" + resourceTable.getTableSize());
137         System.out.println(" -capacity=" + resourceTable.getTableCapacity());
138         System.out.println(" -count=" + resourceTable.getTableCount());
139         System.out.println(" -size=" + resourceTable.getTableSize());
140         //resourceTable.analyse();
141     }
142     public void checkDirectReference(int dr)
143     throws DatabaseException {
144         if (deleted) return;
145         if (!ClusterTraits.statementIndexIsDirect(dr))
146             throw new ValidationException("Reference is not direct. Reference=" + dr);
147         if (ClusterTraits.isFlat(dr))
148             throw new ValidationException("Reference is flat. Reference=" + dr);
149         if (ClusterTraits.isLocal(dr)) {
150             if (dr < 1 || dr > resourceTable.getUsedSize())
151                 throw new ValidationException("Illegal local reference. Reference=" + dr);
152         } else {
153             int fi = ClusterTraits.getForeignIndexFromReference(dr);
154             int ri = ClusterTraits.getResourceIndexFromForeignReference(dr);
155             if (fi < 1 || fi > foreignTable.getUsedSize())
156                 throw new ValidationException("Illegal foreign reference. Reference=" + dr + " foreign index=" + fi);
157             if (ri < 1 || ri > ClusterTraits.getMaxNumberOfResources())
158                 throw new ValidationException("Illegal foreign reference. Reference=" + dr + " resource index=" + ri);
159         }
160     }
161     public void checkPredicateIndex(int pi)
162     throws DatabaseException {
163         if (deleted) return;
164         //        predicateTable.checkPredicateSetIndex(this, pi);
165     }
166     public void checkObjectSetReference(int or)
167     throws DatabaseException {
168         if (deleted) return;
169         if (ClusterTraits.statementIndexIsDirect(or))
170             throw new ValidationException("Illegal object set reference. Reference=" + or);
171         int oi = ClusterTraits.statementIndexGet(or);
172         this.objectTable.checkObjectSetIndex(this, oi);
173     }
174
175     public void checkValueInit()
176     throws DatabaseException {
177         valueTable.checkValueInit();
178     }
179     public void checkValue(int capacity, int index)
180     throws DatabaseException {
181         valueTable.checkValue(capacity, index);
182     }
183     public void checkValueFini()
184     throws DatabaseException {
185         valueTable.checkValueFini();
186     }
187     public void checkForeingIndex(int fi)
188     throws DatabaseException {
189         if (deleted) return;
190         if (fi<1 || fi > foreignTable.getUsedSize())
191             throw new ValidationException("Illegal foreign index=" + fi);
192     }
193     public void checkCompleteSetReference(int cr)
194     throws DatabaseException {
195         if (!ClusterTraits.completeReferenceIsMultiple(cr))
196             throw new ValidationException("Illegal complete set reference. Reference=" + cr);
197         int ci = cr;
198         this.completeTable.checkCompleteSetIndex(this, ci);
199     }
200     public void check()
201     throws DatabaseException {
202         if (deleted) return;
203 //        this.completeTable.check(this);
204 //        this.objectTable.check(this);
205 //        // Must be after object table check.
206 //        this.predicateTable.check(this);
207 //        this.resourceTable.check(this);
208     }
209     @Override
210     public CompleteTypeEnum getCompleteType(int resourceKey, ClusterSupport support)
211     throws DatabaseException {
212         if (deleted) return CompleteTypeEnum.NotComplete;
213         final int resourceRef = getLocalReference(resourceKey);
214         CompleteTypeEnum ct = resourceTable.getCompleteType(resourceRef);
215         if (DEBUG)
216             System.out.println("ClusterSmall.getCompleteType rk=" + resourceKey + " ct=" + ct);
217         return ct;
218     }
219
220     @Override
221     public int getCompleteObjectKey(int resourceKey, ClusterSupport support)
222     throws DatabaseException {
223         if (deleted) return 0;
224         final int resourceIndexOld = getLocalReference(resourceKey);
225         short completeRef = resourceTable.getCompleteObjectRef(resourceIndexOld);
226         int clusterIndex;
227         int resourceIndex;
228         if (0 == completeRef)
229             throw new DatabaseException("Resource's complete object refernce is null. Resource key=" + resourceKey + ".");
230         ClusterI.CompleteTypeEnum completeType = resourceTable.getCompleteType(resourceIndexOld);
231         if (completeType == ClusterI.CompleteTypeEnum.NotComplete)
232             throw new DatabaseException("Resource has multiple complete objects. Resource key=" + resourceKey + ".");
233         if (ClusterTraitsSmall.resourceRefIsLocal(completeRef)) {
234             clusterIndex = clusterKey;
235             resourceIndex = completeRef;
236         } else { // Resource has one complete statement.
237             ResourceUID resourceUID = clusterMap.getForeignResourceUID(completeRef);
238             ClusterI c = support.getClusterByClusterUIDOrMake(resourceUID.asCID());
239             clusterIndex = c.getClusterKey();
240             resourceIndex = resourceUID.getIndex();
241         }
242         int key = ClusterTraits.createResourceKey(clusterIndex, resourceIndex);
243         if (DEBUG)
244             System.out.println("ClusterSmall.complete object rk=" + resourceKey + " ck=" + key);
245         return key;
246     }
247
248     @Override
249     public boolean isComplete(int resourceKey, ClusterSupport support)
250     throws DatabaseException {
251         if (deleted) return false;
252         final int resourceRef = getLocalReference(resourceKey);
253         final ClusterI.CompleteTypeEnum completeType = resourceTable.getCompleteType(resourceRef);
254         boolean complete = completeType != ClusterI.CompleteTypeEnum.NotComplete;
255         if (DEBUG)
256             System.out.println("ClusterSmall.key=" + resourceKey + " isComplete=" + complete);
257         return complete;
258     }
259     public int getSingleObject(int resourceKey, int predicateKey, int objectIndex, ClusterSupport support) throws DatabaseException {
260         if (DEBUG)
261             System.out.println("ClusterSmall.getSingleObject: rk=" + resourceKey + " pk=" + predicateKey);
262         if (deleted) return 0;
263         if (0 == objectIndex) {
264             final int resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(resourceKey);
265             final short pRef = getInternalReferenceOrZero2(predicateKey, support);
266             final ClusterI.CompleteTypeEnum pCompleteType = ClusterTraitsBase.getCompleteTypeFromResourceKey(predicateKey);
267             return resourceTable.getSingleObject(resourceIndex, support, pRef, pCompleteType, completeTable, this);
268         }
269         return objectTable.getSingleObject(objectIndex, support, this);
270     }
271
272     public void forObjects(ReadGraphImpl graph, int resourceKey, int predicateKey, int objectIndex, AsyncMultiProcedure<Resource> procedure,
273             ClusterSupport support) throws DatabaseException {
274         if (deleted) return;
275         if (DEBUG)
276             System.out.println("ClusterSmall.forObjects1: rk=" + resourceKey + " pk=" + predicateKey);
277         if (0 == objectIndex) {
278             final int resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKey(resourceKey);
279             final int pRef = getInternalReferenceOrZero2(predicateKey, support);
280             final ClusterI.CompleteTypeEnum pCompleteType = ClusterTraitsBase.getCompleteTypeFromResourceKey(predicateKey);
281             resourceTable.foreachObject(resourceIndex, graph, procedure, support, pRef, pCompleteType, completeTable, this);
282             return;
283         }
284         objectTable.foreachObject(graph, objectIndex, procedure, this);
285     }
286
287     public <C> void forObjects(ReadGraphImpl graph, int resourceKey, int predicateKey, int objectIndex, C context, AsyncContextMultiProcedure<C, Resource> procedure,
288             ClusterSupport support) throws DatabaseException {
289         if (DEBUG)
290             System.out.println("ClusterSmall.forObjects1: rk=" + resourceKey + " pk=" + predicateKey);
291         if (deleted) return;
292         if (0 == objectIndex) {
293             final int resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKey(resourceKey);
294             final int pRef = getInternalReferenceOrZero2(predicateKey, support);
295             final ClusterI.CompleteTypeEnum pCompleteType = ClusterTraitsBase.getCompleteTypeFromResourceKey(predicateKey);
296             resourceTable.foreachObject(resourceIndex, graph, context, procedure, support, pRef, pCompleteType, completeTable, this);
297             return;
298         }
299         objectTable.foreachObject(graph, objectIndex, context, procedure, this);
300     }
301
302     @Override
303     public <Context> boolean forObjects(int resourceKey, int predicateKey, int objectIndex, ObjectProcedure<Context> procedure,
304             Context context, ClusterSupport support) throws DatabaseException {
305         if (DEBUG)
306             System.out.println("ClusterSmall.forObjects2: rk=" + resourceKey + " pk=" + predicateKey);
307         if (deleted) return false;
308         if (0 == objectIndex) {
309             final int resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKey(resourceKey);
310             final short pRef = getInternalReferenceOrZero2(predicateKey, support);
311             final ClusterI.CompleteTypeEnum pCompleteType = ClusterTraitsBase.getCompleteTypeFromResourceKey(predicateKey);
312             return resourceTable.foreachObject(resourceIndex, procedure, context, support, this, pRef, pCompleteType, completeTable);
313         }
314         return objectTable.foreachObject(objectIndex, procedure, context, support, this);
315     }
316
317     @Override
318     public int getSingleObject(int resourceKey, int predicateKey, ClusterSupport support) throws DatabaseException {
319         if (DEBUG)
320             System.out.println("ClusterSmall.getSingleObject2: rk=" + resourceKey + " pk=" + predicateKey);
321         if (deleted) return 0;
322         final int resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKey(resourceKey);
323         final short pRef = getInternalReferenceOrZero2(predicateKey, support);
324         final int completeType = ClusterTraitsBase.getCompleteTypeIntFromResourceKey(predicateKey);
325         final ClusterI.CompleteTypeEnum pCompleteType = CompleteTypeEnum.make(completeType);
326         if (completeType > 0)
327             return resourceTable.getSingleObject(resourceIndex, support, pRef, pCompleteType, completeTable, this);
328         final int predicateIndex = (int)resourceTable.table[(resourceIndex<<1) - 1 + resourceTable.offset] & 0xFFFFFF;
329         if (0 == predicateIndex) // All relevant data is in resource table.
330             return resourceTable.getSingleObject(resourceIndex, support, pRef, pCompleteType, completeTable, this);
331         int objectIndex = predicateTable.getObjectIndex(predicateIndex, pRef & 0xFFFF);
332         return getSingleObject(resourceKey, predicateKey, objectIndex, support);
333     }
334
335     @Override
336     public <T> int getSingleObject(int resourceKey, ForPossibleRelatedValueProcedure<T> procedure, ClusterSupport support) throws DatabaseException {
337         if (deleted) return 0;
338         final short resourceIndex = (short)ClusterTraitsBase.getResourceIndexFromResourceKey(resourceKey);
339         final int predicateKey = procedure.predicateKey;
340         int clusterKey = ClusterTraitsBase.getClusterMaskFromResourceKey(resourceKey);
341         short pRef = 0;
342         if(procedure.clusterKey[0] == clusterKey) {
343             pRef = (short)procedure.predicateReference[0];
344         } else {
345             pRef = getInternalReferenceOrZero2(predicateKey, support);
346             procedure.clusterKey[0] = clusterKey;
347             procedure.predicateReference[0] = pRef;
348         }
349
350         final ClusterI.CompleteTypeEnum pCompleteType = procedure.completeType;
351         if (CompleteTypeEnum.NotComplete != pCompleteType)
352             return resourceTable.getSingleObject(resourceIndex, support, pRef, pCompleteType, completeTable, this);
353         final int predicateIndex = (int)resourceTable.table[(resourceIndex<<1) - 1 + resourceTable.offset] & 0xFFFFFF;
354         if (0 == predicateIndex) // All relevant data is in resource table.
355             return resourceTable.getSingleObject(resourceIndex, support, pRef, pCompleteType, completeTable, this);
356         int objectIndex = predicateTable.getObjectIndex(predicateIndex, pRef & 0xFFFF);
357         return getSingleObject(resourceKey, predicateKey, objectIndex, support);
358     }
359
360     @Override
361     public <C, T> int getSingleObject(int resourceKey, ForPossibleRelatedValueContextProcedure<C, T> procedure, ClusterSupport support) throws DatabaseException {
362         if (deleted) return 0;
363         final short resourceIndex = (short)ClusterTraitsBase.getResourceIndexFromResourceKey(resourceKey);
364         final int predicateKey = procedure.predicateKey;
365         int clusterKey = ClusterTraitsBase.getClusterMaskFromResourceKey(resourceKey);
366         short pRef = 0;
367         if(procedure.clusterKey[0] == clusterKey) {
368             pRef = (short)procedure.predicateReference[0];
369         } else {
370             pRef = getInternalReferenceOrZero2(predicateKey, support);
371             procedure.clusterKey[0] = clusterKey;
372             procedure.predicateReference[0] = pRef;
373         }
374         final ClusterI.CompleteTypeEnum pCompleteType = procedure.completeType;
375         if (CompleteTypeEnum.NotComplete != pCompleteType)
376             return resourceTable.getSingleObject(resourceIndex, support, pRef, pCompleteType, completeTable, this);
377         final int predicateIndex = (int)resourceTable.table[(resourceIndex<<1) - 1 + resourceTable.offset] & 0xFFFFFF;
378         if (0 == predicateIndex) // All relevant data is in resource table.
379             return resourceTable.getSingleObject(resourceIndex, support, pRef, pCompleteType, completeTable, this);
380         int objectIndex = predicateTable.getObjectIndex(predicateIndex, pRef & 0xFFFF);
381         return getSingleObject(resourceKey, predicateKey, objectIndex, support);
382     }
383
384     @Override
385     public void forObjects(ReadGraphImpl graph, int resourceKey,
386             int predicateKey, AsyncMultiProcedure<Resource> procedure) throws DatabaseException {
387         if (deleted) return;
388         SessionImplSocket session = (SessionImplSocket)graph.getSession();
389         ClusterSupport support = session.clusterTranslator;
390         if (DEBUG)
391             System.out.println("ClusterSmall.forObjects3: rk=" + resourceKey + " pk=" + predicateKey);
392         final int resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKey(resourceKey);
393         final int pRef = getInternalReferenceOrZero2(predicateKey, support);
394         final int completeType = ClusterTraitsBase.getCompleteTypeIntFromResourceKey(predicateKey);
395         final ClusterI.CompleteTypeEnum pCompleteType = CompleteTypeEnum.make(completeType);
396         if (completeType > 0) {
397             resourceTable.foreachObject(resourceIndex, graph, procedure, support, pRef, pCompleteType, completeTable, this);
398             return;
399         }
400         final int predicateIndex = (int)resourceTable.table[(resourceIndex<<1) - 1 + resourceTable.offset] & 0xFFFFFF;
401         if (0 == predicateIndex) {
402             resourceTable.foreachObject(resourceIndex, graph, procedure, support, pRef, pCompleteType, completeTable, this);
403             return;
404         }
405         int objectIndex = predicateTable.getObjectIndex(predicateIndex, pRef & 0xFFFF);
406         forObjects(graph, resourceKey, predicateKey, objectIndex, procedure, support);
407     }
408
409     public void forObjects(ReadGraphImpl graph, int resourceKey, ForEachObjectProcedure procedure) throws DatabaseException {
410         if (deleted) return;
411         final int resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKey(resourceKey);
412         final int predicateKey = procedure.predicateKey;
413         int clusterKey = ClusterTraitsBase.getClusterMaskFromResourceKey(resourceKey);
414         int pRef = 0;
415         if(procedure.clusterKey[0] == clusterKey) {
416             pRef = procedure.predicateReference[0];
417         } else {
418             SessionImplSocket session = (SessionImplSocket)graph.getSession();
419             ClusterSupport support = session.clusterTranslator;
420             pRef = getInternalReferenceOrZero2(predicateKey, support);
421             procedure.clusterKey[0] = clusterKey;
422             procedure.predicateReference[0] = pRef;
423         }
424         final ClusterI.CompleteTypeEnum pCompleteType = procedure.completeType;
425         if (ClusterI.CompleteTypeEnum.NotComplete != pCompleteType) {
426             SessionImplSocket session = (SessionImplSocket)graph.getSession();
427             ClusterSupport support = session.clusterTranslator;
428             resourceTable.foreachObject(resourceIndex, graph, procedure, support, pRef, pCompleteType, completeTable, this);
429             return;
430         }
431         final int predicateIndex = (int)resourceTable.table[(resourceIndex<<1) - 1 + resourceTable.offset] & 0xFFFFFF;
432         if (0 == predicateIndex) {
433             SessionImplSocket session = (SessionImplSocket)graph.getSession();
434             ClusterSupport support = session.clusterTranslator;
435             resourceTable.foreachObject(resourceIndex, graph, procedure, support, pRef, pCompleteType, completeTable, this);
436             return;
437         }
438         int hashBase = predicateIndex + predicateTable.offset;
439         if (predicateTable.table[hashBase-1] < 0) {
440             int objectIndex = TableIntArraySet2.get(predicateTable.table, hashBase, pRef & 0xFFFF);
441             //int objectIndex = predicateTable.getObjectIndex(predicateIndex, pRef & 0xFFFF);
442             SessionImplSocket session = (SessionImplSocket)graph.getSession();
443             ClusterSupport support = session.clusterTranslator;
444             forObjects(graph, resourceKey, predicateKey, objectIndex, procedure, support);
445         } else {
446             procedure.finished(graph);
447 //            graph.dec();
448         }
449 }
450
451     public <C> void forObjects(ReadGraphImpl graph, int resourceKey, C context, ForEachObjectContextProcedure<C> procedure) throws DatabaseException {
452         if (deleted) return;
453         final int resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKey(resourceKey);
454         final int predicateKey = procedure.predicateKey;
455         int clusterKey = ClusterTraitsBase.getClusterMaskFromResourceKey(resourceKey);
456         int pRef = 0;
457         if(procedure.clusterKey[0] == clusterKey) {
458             pRef = procedure.predicateReference[0];
459         } else {
460             SessionImplSocket session = (SessionImplSocket)graph.getSession();
461             ClusterSupport support = session.clusterTranslator;
462             pRef = getInternalReferenceOrZero2(predicateKey, support);
463             procedure.clusterKey[0] = clusterKey;
464             procedure.predicateReference[0] = pRef;
465         }
466
467         final ClusterI.CompleteTypeEnum pCompleteType = procedure.completeType;
468         if (ClusterI.CompleteTypeEnum.NotComplete != pCompleteType) {
469             SessionImplSocket session = (SessionImplSocket)graph.getSession();
470             ClusterSupport support = session.clusterTranslator;
471             resourceTable.foreachObject(resourceIndex, graph, context, procedure, support, pRef, pCompleteType, completeTable, this);
472             return;
473         }
474         final int predicateIndex = (int)resourceTable.table[(resourceIndex<<1) - 1 + resourceTable.offset] & 0xFFFFFF;
475         if (0 == predicateIndex) {
476             SessionImplSocket session = (SessionImplSocket)graph.getSession();
477             ClusterSupport support = session.clusterTranslator;
478             resourceTable.foreachObject(resourceIndex, graph, context, procedure, support, pRef, pCompleteType, completeTable, this);
479             return;
480         }
481         int hashBase = predicateIndex + predicateTable.offset;
482         if(predicateTable.table[hashBase-1] < 0) {
483             int objectIndex = TableIntArraySet2.get(predicateTable.table, hashBase, pRef & 0xFFFF);
484             SessionImplSocket session = (SessionImplSocket)graph.getSession();
485             ClusterSupport support = session.clusterTranslator;
486             forObjects(graph, resourceKey, predicateKey, objectIndex, context, procedure, support);
487         } else {
488             int objectIndex = TableIntSet2.get(predicateTable.table, hashBase, pRef & 0xFFFF);
489             SessionImplSocket session = (SessionImplSocket)graph.getSession();
490             ClusterSupport support = session.clusterTranslator;
491             forObjects(graph, resourceKey, predicateKey, objectIndex, context, procedure, support);
492         }
493     }
494     @Override
495     public <Context> boolean forObjects(int resourceKey, int predicateKey,
496             ObjectProcedure<Context> procedure, Context context, ClusterSupport support)
497     throws DatabaseException {
498         if (DEBUG)
499             System.out.println("ClusterSmall.forObjects4: rk=" + resourceKey + " pk=" + predicateKey);
500         if (deleted) return false;
501         final short resourceIndex = (short)ClusterTraitsBase.getResourceIndexFromResourceKey(resourceKey);
502         final short pRef = getInternalReferenceOrZero2(predicateKey, support);
503         final ClusterI.CompleteTypeEnum pCompleteType = ClusterTraitsBase.getCompleteTypeFromResourceKey(predicateKey);
504         // PredicateType is complete i.e. all relevant data is in resource table.
505         if (ClusterI.CompleteTypeEnum.NotComplete != pCompleteType) {
506             if (DEBUG)
507                 System.out.println("ClusterSmall.forObjects: complete type was " + pCompleteType + " cluster=" + getClusterUID());
508             return resourceTable.foreachObject(resourceIndex, procedure, context, support, this, pRef, pCompleteType, completeTable);
509         }
510         final int predicateIndex = resourceTable.getPredicateIndex(resourceIndex);
511         if (0 == predicateIndex) { // All relevant data is in resource table.
512             if (DEBUG)
513                 System.out.println("ClusterSmall.forObjects: no predicate table " + pCompleteType);
514             return resourceTable.foreachObject(resourceIndex, procedure, context, support, this, pRef, pCompleteType, completeTable);
515         }
516         int objectIndex = predicateTable.getObjectIndex(predicateIndex, pRef & 0xFFFF);
517         return forObjects(resourceKey, predicateKey, objectIndex, procedure, context, support);
518     }
519     @Override
520     public <Context> boolean forPredicates(int resourceKey,
521             PredicateProcedure<Context> procedure, Context context, ClusterSupport support)
522     throws DatabaseException {
523         if (DEBUG)
524             System.out.println("ClusterSmall.forPredicates: rk=" + resourceKey );
525         if (deleted) return false;
526         final int resourceIndex = getLocalReference(resourceKey);
527         final int predicateIndex = resourceTable.getPredicateIndex(resourceIndex);
528         if (0 == predicateIndex)
529             return resourceTable.foreachPredicate(resourceIndex,
530                     procedure, context, support, this, completeTable);
531         else {
532             boolean broken = resourceTable.foreachPredicate(resourceIndex,
533                     procedure, context, support, this, completeTable);
534             if (broken)
535                 return true;
536         }
537         return predicateTable.foreachPredicate(predicateIndex,
538                 procedure, context, support, this);
539     }
540     @Override
541     public ClusterI addRelation(int sResourceKey, int pResourceKey, int oResourceKey, ClusterSupport support)
542     throws DatabaseException {
543         if (DEBUG)
544             System.out.println("add rk=" + sResourceKey + " pk=" + pResourceKey + " ok=" + oResourceKey);
545         if (deleted) return null;
546         if(proxy) {
547             ClusterImpl cluster = clusterTable.load2(clusterId, clusterKey);
548             return cluster.addRelation(sResourceKey, pResourceKey, oResourceKey, support);
549         }
550
551         //        check();
552         boolean ret;
553         try {
554             short sri = getLocalReferenceAnd(sResourceKey, support, ClusterChange.ADD_OPERATION);
555             short pri = getReferenceOrCreateIfForeign(pResourceKey, support, ClusterStream.NULL_OPERATION);
556             short ori = getReferenceOrCreateIfForeign(oResourceKey, support, ClusterStream.NULL_OPERATION);
557             ClusterI.CompleteTypeEnum completeType = ClusterTraitsBase.getCompleteTypeFromResourceKey(pResourceKey);
558             ret = addRelationInternal(sri, pri, ori, completeType);
559             calculateModifiedId();
560         } catch (OutOfSpaceException e) {
561             boolean streamOff = support.getStreamOff();
562             if (!streamOff) {
563                 support.cancelStatement(this);
564                 support.setStreamOff(true);
565             }
566             ClusterI cluster = toBig(support);
567             if (!streamOff)
568                 support.setStreamOff(false);
569             ClusterI cluster2 = cluster.addRelation(sResourceKey, pResourceKey, oResourceKey, support);
570             if (cluster != cluster2)
571                 throw new DatabaseException("Internal error. Contact application support.");
572             return cluster;
573         }
574 //        check();
575         if (ret) {
576             support.addStatement(this);
577             return this;
578         } else {
579             support.cancelStatement(this);
580             return null;
581         }
582     }
583     @Override
584     public boolean removeRelation(int sResourceKey, int pResourceKey, int oResourceKey, ClusterSupport support)
585     throws DatabaseException {
586         if (deleted) return false;
587         short sri = getLocalReferenceAnd(sResourceKey, support, ClusterChange.REMOVE_OPERATION);
588         short pri = getInternalReferenceOrZeroAnd(pResourceKey, support, ClusterStream.NULL_OPERATION);
589         short ori = getInternalReferenceOrZeroAnd(oResourceKey, support, ClusterStream.NULL_OPERATION);
590         boolean ret = false;
591         if (0 != pri && 0 != ori) {
592             ClusterI.CompleteTypeEnum completeType = ClusterTraitsBase.getCompleteTypeFromResourceKey(pResourceKey);
593             ret = removeRelationInternal(sri, pri, ori, completeType, support);
594             calculateModifiedId();
595         }
596         if (ret)
597             support.removeStatement(this);
598         else
599             support.cancelStatement(this);
600         //        check();
601         return ret;
602     }
603     @Override
604     public void denyRelation(int sResourceKey, int pResourceKey, int oResourceKey, ClusterSupport support)
605     throws DatabaseException {
606         if (deleted) return;
607         short s = checkResourceKeyIsOursAndGetResourceIndexIf(sResourceKey, support);
608         ResourceReferenceAndCluster p = checkResourceKeyAndGetResourceIndexIf(pResourceKey, support);
609         ResourceReferenceAndCluster o = checkResourceKeyAndGetResourceIndexIf(oResourceKey, support);
610         if (0 == s || 0 == p.reference || 0 == o.reference)
611             return;
612         //        check();
613         ClusterI.CompleteTypeEnum completeType = ClusterTraitsBase.getCompleteTypeFromResourceKey(pResourceKey);
614         boolean ret = removeRelationInternal(s, p.reference, o.reference, completeType, support);
615         if (ret) {
616             support.addStatementIndex(this, sResourceKey, getClusterUID(), ClusterChange.REMOVE_OPERATION);
617             support.addStatementIndex(this, pResourceKey, p.clusterUID, ClusterStream.NULL_OPERATION);
618             support.addStatementIndex(this, oResourceKey, o.clusterUID, ClusterStream.NULL_OPERATION);
619             support.removeStatement(this);
620         }
621         calculateModifiedId();
622         //        check();
623         return;
624     }
625     @Override
626     public InputStream getValueStream(int resourceKey, ClusterSupport support) throws DatabaseException {
627         if (DEBUG)
628             System.out.println("ClusterSmall.getValue " + resourceKey);
629         if (deleted) return null;
630         int resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(resourceKey);
631         try {
632             byte[] buffer = resourceTable.getValue(valueTable, resourceIndex);
633             if(buffer == null) return null;
634             return new ByteArrayInputStream(buffer);
635         } catch (ExternalValueException e) {
636             return support.getValueStreamEx(resourceIndex, clusterId);
637         }
638     }
639     @Override
640     public byte[] getValue(int resourceKey, ClusterSupport support)
641     throws DatabaseException {
642         if (DEBUG)
643             System.out.println("ClusterSmall.getValue " + resourceKey);
644         if (deleted) return null;
645         int resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(resourceKey);
646         try {
647             return resourceTable.getValue(valueTable, resourceIndex);
648         } catch (ExternalValueException e) {
649             return support.getValueEx(resourceIndex, clusterId);
650         }
651     }
652     @Override
653     public boolean hasValue(int resourceKey, ClusterSupport support)
654     throws DatabaseException {
655         if (deleted) return false;
656         int resourceIndex = getLocalReference(resourceKey);
657         return resourceTable.hasValue(resourceIndex);
658     }
659     @Override
660     public boolean removeValue(int resourceKey, ClusterSupport support)
661     throws DatabaseException {
662         if (deleted) return false;
663         int resourceIndex = getLocalReferenceAnd(resourceKey, support, ClusterChange.DELETE_OPERATION);
664         support.removeValue(this);
665         calculateModifiedId();
666         return resourceTable.removeValue(valueTable, resourceIndex);
667     }
668     @Override
669     public ClusterI setValue(int rResourceId, byte[] value, int length, ClusterSupport support)
670     throws DatabaseException {
671         if (deleted) return null;
672         int resourceIndex = getLocalReferenceAnd(rResourceId, support, ClusterStream.SET_OPERATION);
673         support.setValue(this, getClusterId(), value, length);
674         try {
675             resourceTable.setValue(valueTable, resourceIndex, value, length);
676             calculateModifiedId();
677             return this;
678         } catch (OutOfSpaceException e) {
679             boolean streamOff = support.getStreamOff();
680             if (!streamOff)
681                 support.setStreamOff(true);
682             ClusterI cluster = toBig(support);
683             cluster.setValue(rResourceId, value, length, support);
684             if (!streamOff)
685                 support.setStreamOff(false);
686             return cluster;
687         }
688     }
689     @Override
690     public ClusterI modiValueEx(int rResourceId, long voffset, int length, byte[] value, int offset, ClusterSupport support)
691     throws DatabaseException {
692         if (deleted) return null;
693         int resourceIndex = getLocalReferenceAnd(rResourceId, support, ClusterStream.MODI_OPERATION);
694         support.modiValue(this, getClusterId(), voffset, length, value, offset);
695         resourceTable.setValueEx(valueTable, resourceIndex);
696         calculateModifiedId();
697         return this;
698     }
699     @Override
700     public byte[] readValueEx(int rResourceId, long voffset, int length, ClusterSupport support)
701     throws DatabaseException {
702         if (deleted) return null;
703         int resourceIndex = getLocalReference(rResourceId);
704         boolean isExternal = resourceTable.isValueEx(valueTable, resourceIndex);
705         if (!isExternal)
706             throw new DatabaseException("ClusterI.readValue supported only for external value. Resource key=" + rResourceId);
707         return support.getValueEx(resourceIndex, getClusterId(), voffset, length);
708     }
709     @Override
710     public long getValueSizeEx(int rResourceId, ClusterSupport support)
711     throws DatabaseException, ExternalValueException {
712         if (deleted) return 0;
713         int resourceIndex = getLocalReference(rResourceId);
714         boolean isExternal = resourceTable.isValueEx(valueTable, resourceIndex);
715         if (!isExternal)
716             throw new ExternalValueException("ClusterI.getValueSizeEx supported only for external value. Resource key=" + rResourceId);
717         return support.getValueSizeEx(resourceIndex, getClusterId());
718     }
719     @Override
720     public void setValueEx(int rResourceId)
721     throws DatabaseException {
722         int resourceIndex = getLocalReference(rResourceId);
723         resourceTable.setValueEx(valueTable, resourceIndex);
724     }
725     @Override
726     public int createResource(ClusterSupport support)
727     throws DatabaseException {
728         if (deleted) return 0;
729         if(proxy) {
730             ClusterImpl cluster = clusterTable.load2(clusterId, clusterKey);
731             return cluster.createResource(support);
732         }
733
734         short resourceIndex = resourceTable.createResource();
735         calculateModifiedId();
736         if(DebugPolicy.REPORT_RESOURCE_ID_ALLOCATION)
737             System.out.println("[RID_ALLOCATION]: ClusterSmall[" + clusterId + "] allocates " + resourceIndex);
738         support.createResource(this, resourceIndex, getClusterId());
739         return ClusterTraits.createResourceKey(clusterKey, resourceIndex);
740     }
741     @Override
742     public boolean hasResource(int resourceKey, ClusterSupport support) {
743         if (deleted) return false;
744         int clusterKey = ClusterTraitsBase.getClusterKeyFromResourceKeyNoThrow(resourceKey);
745         if (this.clusterKey != clusterKey) // foreign resource
746             return false;
747         int resourceIndex;
748         try {
749             resourceIndex = ClusterTraits.getResourceIndexFromResourceKey(resourceKey);
750         } catch (DatabaseException e) {
751             return false;
752         }
753         if (resourceIndex > 0 & resourceIndex <= resourceTable.getTableCount())
754             return true;
755         else
756             return false;
757     }
758     @Override
759     public int getNumberOfResources(ClusterSupport support)
760     throws DatabaseException  {
761         if (deleted) return 0;
762         if(proxy) {
763             ClusterImpl cluster = clusterTable.load2(clusterId, clusterKey);
764             return cluster.getNumberOfResources(support);
765         }
766
767         return resourceTable.getUsedSize();
768     }
769
770     public int getNumberOfResources() {
771         if (deleted || proxy)
772             return 0;
773         return resourceTable.getUsedSize();
774     }
775
776     @Override
777     public long getUsedSpace() {
778         if (deleted) return 0;
779         if(isEmpty()) return 0;
780         long rt = resourceTable.getTableCapacity() * 8 + 8; // (8 = cluster id)
781         long ft = foreignTable.getTableCapacity() * 8;
782         long pt = predicateTable.getTableCapacity() * 4;
783         long ot = objectTable.getTableCapacity() * 4;
784         long ct = completeTable.getTableCapacity() * 4;
785         long vt = valueTable.getTableCapacity() * 1;
786         long cm = clusterMap.getUsedSpace();
787         return rt + ft + pt + ot + ct + vt + cm;
788     }
789     @Override
790     public boolean isEmpty() {
791         if (deleted) return true; // Deleted cluster is always empty.
792         if(resourceTable == null) return true;
793         return resourceTable.getTableCount() == 0;
794     }
795     @Override
796     public void printDebugInfo(String message, ClusterSupport support)
797     throws DatabaseException {
798         if (deleted) return;
799         throw new DatabaseException("Not implemented!");
800     }
801     private short getInternalReferenceOrZero2(int resourceKey, ClusterSupport support) throws DatabaseException {
802         int resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(resourceKey);
803         if (!ClusterTraitsBase.isCluster(clusterBits, resourceKey)) {
804             return clusterMap.getForeignReferenceOrZero(resourceKey);
805         } else {
806             return (short)resourceIndex;
807         }
808     }
809     private short getInternalReferenceOrZeroAnd(int resourceKey, ClusterSupport support, byte op)
810     throws DatabaseException {
811         int clusterKey = ClusterTraits.getClusterKeyFromResourceKey(resourceKey);
812         int resourceIndex = ClusterTraits.getResourceIndexFromResourceKey(resourceKey);
813         if (this.clusterKey != clusterKey) { // foreign resource
814             ClusterI foreignCluster = support.getClusterByClusterKey(clusterKey);
815             ClusterUID clusterUID = foreignCluster.getClusterUID();
816             short foreignRef = clusterMap.getForeignReferenceOrZero(resourceKey);
817             support.addStatementIndex(this, resourceKey, clusterUID, op);
818             return foreignRef;
819         }
820         support.addStatementIndex(this, resourceKey, getClusterUID(), op);
821         return (short)resourceIndex;
822     }
823     private final short getLocalReference(int resourceKey) throws DatabaseException {
824         return ClusterTraits.getResourceIndexFromResourceKeyNoThrow(resourceKey);
825     }
826     private final short getLocalReferenceAnd(int resourceKey, ClusterSupport support, byte op)
827     throws DatabaseException {
828         short resourceIndex = getLocalReference(resourceKey);
829         support.addStatementIndex(this, resourceKey, getClusterUID(), op);
830         return resourceIndex;
831     }
832     private short checkResourceKeyIsOursAndGetResourceIndexIf(int resourceKey, ClusterSupport support)
833     throws DatabaseException {
834         int clusterShortId = ClusterTraits.getClusterKeyFromResourceKey(resourceKey);
835         if (this.clusterKey != clusterShortId)
836             return 0;
837         int resourceIndex = ClusterTraits.getResourceIndexFromResourceKey(resourceKey);
838         return (short)resourceIndex;
839     }
840     private short getReferenceOrCreateIfForeign(int resourceKey, ClusterSupport support, byte op)
841     throws DatabaseException {
842         int clusterKey = ClusterTraits.getClusterKeyFromResourceKey(resourceKey);
843         short resourceIndex = (short)ClusterTraits.getResourceIndexFromResourceKey(resourceKey);
844         if (this.clusterKey != clusterKey) {
845             ClusterI foreignCluster = support.getClusterByClusterKey(clusterKey);
846             ClusterUID clusterUID = foreignCluster.getClusterUID();
847             support.addStatementIndex(this, resourceKey, clusterUID, op);
848             short ref = clusterMap.getForeignReferenceOrCreateByResourceKey(resourceKey, clusterUID);
849             return ref;
850         }
851         support.addStatementIndex(this, resourceKey, getClusterUID(), op);
852         return resourceIndex;
853     }
854     private class ResourceReferenceAndCluster {
855         ResourceReferenceAndCluster(short reference, ClusterUID clusterUID) {
856             this.reference = reference;
857             this.clusterUID = clusterUID;
858         }
859         public final short reference;
860         public final ClusterUID clusterUID;
861     }
862     private ResourceReferenceAndCluster checkResourceKeyAndGetResourceIndexIf(int resourceKey, ClusterSupport support)
863     throws DatabaseException {
864         int clusterKey = ClusterTraits.getClusterKeyFromResourceKey(resourceKey);
865         short resourceIndex = (short)ClusterTraits.getResourceIndexFromResourceKey(resourceKey);
866         if (this.clusterKey != clusterKey) { // foreign resource
867             ClusterI foreignCluster = support.getClusterByClusterKey(clusterKey);
868             ClusterUID clusterUID = foreignCluster.getClusterUID();
869             short ref = clusterMap.getForeignReferenceOrZero(resourceKey);
870             return new ResourceReferenceAndCluster(ref, clusterUID);
871         }
872         return new ResourceReferenceAndCluster(resourceIndex, getClusterUID());
873     }
874
875     static long fTime = 0;
876     
877     int executeIndex = 0;
878     long clusterUID1 = 0;
879     long clusterUID2 = 0;
880
881     @Override
882     final public int execute(int resourceReference) throws DatabaseException {
883         
884         if (deleted) return 0;
885         short resourceRef = (short)resourceReference;
886         int key;
887         if (ClusterTraitsSmall.resourceRefIsLocal(resourceRef)) {
888             key = clusterBits | resourceRef;
889         } else {
890             foreignTable.fillResourceUID(ClusterTraitsSmall.resourceRefGetForeignIndex((short)resourceRef), this);
891             key = ClusterTraitsBase.createResourceKey(clusterSupport.getClusterKeyByClusterUIDOrMake(clusterUID1, clusterUID2), executeIndex);
892         }
893         if (DEBUG)
894             System.out.println("ClusterSmall.execute key=" + key);
895         return key;
896     }
897
898     private boolean addRelationInternal(short sReference, short pReference, short oReference, ClusterI.CompleteTypeEnum completeType)
899     throws DatabaseException {
900         int predicateIndex = resourceTable.addStatement(sReference, pReference, oReference, predicateTable, objectTable, completeType, completeTable);
901         if (0 == predicateIndex)
902             return true; // added to resourceTable
903         else if (0 > predicateIndex)
904             return false; // old complete statemenent
905         int newPredicateIndex = predicateTable.addPredicate(predicateIndex, 0xFFFF & pReference, 0xFFFF & oReference, objectTable);
906         if (0 == newPredicateIndex)
907             return false;
908         if (predicateIndex != newPredicateIndex)
909             resourceTable.setPredicateIndex(sReference, newPredicateIndex);
910         return true;
911     }
912     private boolean removeRelationInternal(int sResourceIndex, short pResourceIndex,
913             short oResourceIndex, ClusterI.CompleteTypeEnum completeType, ClusterSupport support)
914     throws DatabaseException {
915         int predicateIndex = resourceTable.getPredicateIndex(sResourceIndex);
916         if (0 == predicateIndex || ClusterI.CompleteTypeEnum.NotComplete != completeType)
917             return resourceTable.removeStatementFromCache(sResourceIndex,
918                     pResourceIndex, oResourceIndex, completeType, completeTable);
919         PredicateTable.Status ret = predicateTable.removePredicate(predicateIndex, 0xFFFF & pResourceIndex, 0xFFFF & oResourceIndex, objectTable);
920         switch (ret) {
921         case NothingRemoved:
922             return false;
923         case PredicateRemoved: {
924             if (0 == predicateTable.getPredicateSetSize(predicateIndex))
925                 resourceTable.setPredicateIndex(sResourceIndex, 0);
926             // intentionally dropping to next case
927         } default:
928             break;
929         }
930         resourceTable.removeStatement(sResourceIndex,
931                 pResourceIndex, oResourceIndex,
932                 completeType, completeTable,
933                 predicateTable, objectTable, support);
934         return true;
935     }
936     @Override
937     public void load() {
938         if (deleted) return;
939         throw new Error("Not supported.");
940     }
941
942     @Override
943     public void load(Consumer<DatabaseException> r) {
944         if (deleted) return;
945         throw new Error("Not supported.");
946     }
947
948     public boolean contains(int resourceKey) {
949         if (deleted) return false;
950         return ClusterTraitsBase.isCluster(clusterBits, resourceKey);
951     }
952     @Override
953     public void load(final ClusterSupport support, final Runnable callback) {
954         if (deleted) return;
955         try {
956             clusterTable.load2(clusterId, clusterKey);
957             callback.run();
958         } catch (DatabaseException e) {
959             e.printStackTrace();
960         }
961
962     }
963     @Override
964     public ClusterI getClusterByResourceKey(int resourceKey,
965             ClusterSupport support) {
966         if (deleted) return null;
967         throw new Error();
968     }
969     @Override
970     public void increaseReferenceCount(int amount) {
971         if (deleted) return;
972         throw new Error();
973     }
974     @Override
975     public void decreaseReferenceCount(int amount) {
976         if (deleted) return;
977         throw new Error();
978     }
979     @Override
980     public int getReferenceCount() {
981         if (deleted) return 0;
982         throw new Error();
983     }
984     @Override
985     public void releaseMemory() {
986     }
987     @Override
988     public void compact() {
989         if (deleted) return;
990         clusterMap.compact();
991     }
992     @Override
993     public boolean isLoaded() {
994         return !proxy || deleted; // Deleted cluster is always loaded.
995     }
996
997     public ClusterImpl tryLoad(SessionImplSocket sessionImpl) throws DatabaseException {
998         if (deleted) return this; // Never load deleted cluster.
999         return clusterTable.tryLoad(clusterId, clusterKey);
1000     }
1001
1002
1003     @Override
1004     public ClusterBig toBig(ClusterSupport support)
1005     throws DatabaseException {
1006         if (DEBUG) {
1007             System.out.println("DEBUG: toBig cluster=" + clusterId);
1008             new Exception().printStackTrace();
1009         }
1010         if (deleted) return null; // Can't convert deleted cluster to big.
1011         ClusterBig big = new ClusterBig(getClusterUID(), clusterKey, support);
1012         big.cc = this.cc;
1013         big.cc.clusterImpl = this;
1014         resourceTable.toBig(big, support, this);
1015         big.foreignLookup = this.foreignLookup;
1016         big.change = this.change;
1017         this.cc = null;
1018         this.foreignLookup = null;
1019         this.change = null;
1020         return big;
1021     }
1022
1023     @Override
1024     public ClusterTypeEnum getType() {
1025         return ClusterTypeEnum.SMALL;
1026     }
1027     @Override
1028     public boolean getImmutable() {
1029         if (deleted) return false;
1030         if (null == resourceTable)
1031             return false;
1032         int status = resourceTable.getClusterStatus();
1033         return (status & ClusterStatus.ImmutableMaskSet) == ClusterStatus.ImmutableMaskSet;
1034     }
1035     @Override
1036     public void setImmutable(boolean immutable, ClusterSupport support) {
1037         if (deleted) return;
1038         if(resourceTable != null) {
1039             int status = resourceTable.getClusterStatus();
1040             if (immutable)
1041                 status |= ClusterStatus.ImmutableMaskSet;
1042             else
1043                 status &= ClusterStatus.ImmutableMaskClear;
1044             resourceTable.setClusterStatus(status);
1045         }
1046         support.setImmutable(this, immutable);
1047     }
1048     @Override
1049     public boolean getDeleted() {
1050         if (deleted) return true;
1051         int status = resourceTable.getClusterStatus();
1052         return (status & ClusterStatus.DeletedMaskSet) == ClusterStatus.DeletedMaskSet;
1053     }
1054     @Override
1055     public void setDeleted(boolean set, ClusterSupport support) {
1056         deleted = set;
1057         if(resourceTable != null) {
1058             int status = resourceTable.getClusterStatus();
1059             if (set)
1060                 status |= ClusterStatus.DeletedMaskSet;
1061             else
1062                 status &= ClusterStatus.DeletedMaskClear;
1063             resourceTable.setClusterStatus(status);
1064         }
1065         if (null != support)
1066             support.setDeleted(this, set);
1067     }
1068     @Override
1069     public String toString() {
1070         if (deleted) return "ClusterSmall[" + getClusterId() + " - has been deleted or hasn't been created.]";
1071         try {
1072             ForeignTableSmall ft = foreignTable;
1073             if (ft == null)
1074                 return "ClusterSmall[" + getClusterId() + " - " + getNumberOfResources() + "]";
1075             TIntShortHashMap map = ft.getResourceHashMap();
1076             TIntHashSet set = new TIntHashSet();
1077             map.forEachKey(value -> {
1078                 set.add(value & 0xfffff000);
1079                 return true;
1080             });
1081             return "ClusterSmall[" + getClusterId() + " - " + getNumberOfResources() + " - " + foreignTable.getResourceHashMap().size() + " - " + set.size() + "]";
1082         } catch (Throwable e) {
1083             return "ClusterSmall[" + getNumberOfResources() + "]";
1084         }
1085     }
1086     @Override
1087     public Table<?> getPredicateTable() {
1088         return predicateTable;
1089     }
1090     @Override
1091     public Table<?> getForeignTable() {
1092         return foreignTable;
1093     }
1094     @Override
1095     public int makeResourceKey(int pRef) throws DatabaseException {
1096         throw new UnsupportedOperationException();
1097     }
1098     @Override
1099     public Table<?> getCompleteTable() {
1100         return completeTable;
1101     }
1102     @Override
1103     public Table<?> getValueTable() {
1104         return valueTable;
1105     }
1106     @Override
1107     public Table<?> getObjectTable() {
1108         return objectTable;
1109     }
1110
1111 }
1112
1113 class ClusterStatus {
1114     public static final int ImmutableMaskClear = 0xFFFFFFFE;
1115     public static final int ImmutableMaskSet = 0x00000001;
1116     public static final int DeletedMaskClear = 0xFFFFFFFD;
1117     public static final int DeletedMaskSet = 0x00000002;
1118 }