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