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