package org.simantics.datatypes.utils; import java.io.ByteArrayInputStream; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.simantics.databoard.Bindings; import org.simantics.databoard.accessor.reference.ChildReference; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.error.RuntimeBindingConstructionException; import org.simantics.databoard.binding.impl.BindingPrintContext; import org.simantics.databoard.binding.mutable.Variant; import org.simantics.databoard.serialization.RuntimeSerializerConstructionException; import org.simantics.databoard.serialization.Serializer; import org.simantics.databoard.util.IdentityPair; import org.simantics.datatypes.DatatypeResource; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; import org.simantics.db.common.primitiverequest.RelatedValue; import org.simantics.db.common.procedure.adapter.TransientCacheListener; import org.simantics.db.common.utils.Logger; import org.simantics.db.exception.DatabaseException; import org.simantics.db.service.Bytes; import org.simantics.db.service.SerialisationSupport; import org.simantics.db.service.XSupport; import org.simantics.layer0.Layer0; import org.simantics.scl.runtime.tuple.Tuple2; import org.simantics.utils.datastructures.Pair; import gnu.trove.list.array.TByteArrayList; import gnu.trove.map.hash.TObjectIntHashMap; /* * Implementation from http://www.geeksforgeeks.org/b-tree-set-3delete/ * */ interface BTreeContentManager { BTreeContentBean getContentBean(ReadGraph graph, Resource node) throws DatabaseException; void setContentBean(WriteGraph graph, Resource node, BTreeContentBean bean) throws DatabaseException; } class PossibleVariantInputStream extends ByteArrayInputStream { PossibleVariantInputStream(byte[] data, int offset) { super(data, offset, data.length-offset); } public int getOffset() { return pos; } } class BTreeContentBinding extends Binding { private final SerialisationSupport ss; private final XSupport xs; public BTreeContentBinding(SerialisationSupport ss, XSupport xs) { this.ss = ss; this.xs = xs; } private final Serializer serializer = new Serializer() { @Override public void serialize(DataOutput out, TObjectIntHashMap identities, Object obj) throws IOException { throw new UnsupportedOperationException(); } @Override public void serialize(DataOutput out, Object obj) throws IOException { byte[] data = serialize(obj); out.write(data); } @Override public Object deserialize(DataInput in, List identities) throws IOException { throw new UnsupportedOperationException(); } @Override public Object deserialize(DataInput in) throws IOException { throw new UnsupportedOperationException(); } @Override public void deserializeTo(DataInput in, List identities, Object dst) throws IOException { throw new UnsupportedOperationException(); } @Override public void deserializeTo(DataInput in, Object dst) throws IOException { throw new UnsupportedOperationException(); } @Override public void skip(DataInput in, List identities) throws IOException { throw new UnsupportedOperationException(); } @Override public void skip(DataInput in) throws IOException { throw new UnsupportedOperationException(); } @Override public Integer getConstantSize() { throw new UnsupportedOperationException(); } @Override public int getSize(Object obj, TObjectIntHashMap identities) throws IOException { throw new UnsupportedOperationException(); } @Override public int getSize(Object obj) throws IOException { return serialize(obj).length; } @Override public int getMinSize() { throw new UnsupportedOperationException(); } private void addBE(TByteArrayList bytes, int value) { bytes.add((byte) ((value >>> 24) & 0xFF)); bytes.add((byte) ((value >>> 16) & 0xFF)); bytes.add((byte) ((value >>> 8) & 0xFF)); bytes.add( (byte) (value & 0xFF)); } private void addBE(TByteArrayList bytes, long value) { bytes.add((byte) ((value >>> 56) & 0xFF)); bytes.add((byte) ((value >>> 48) & 0xFF)); bytes.add((byte) ((value >>> 40) & 0xFF)); bytes.add((byte) ((value >>> 32) & 0xFF)); bytes.add((byte) ((value >>> 24) & 0xFF)); bytes.add((byte) ((value >>> 16) & 0xFF)); bytes.add((byte) ((value >>> 8) & 0xFF)); bytes.add( (byte) (value & 0xFF)); } public byte[] serialize(Object obj) throws IOException { BTreeContentBean bean = (BTreeContentBean)obj; TByteArrayList bytes = new TByteArrayList(); bytes.add(bean.leaf ? (byte)1 : (byte)0); addBE(bytes, bean.n); //addLE(bytes, (bean.key.length+1)/2); Binding pbv = PossibleVariant.BINDING; Serializer pbs = pbv.serializer(); addBE(bytes, bean.key.length); for(PossibleVariant l : bean.key) { byte[] ser = pbs.serialize(l); bytes.add(ser); } addBE(bytes, bean.value.length); for(PossibleResource pr : bean.value) { if(pr.r != null) { bytes.add((byte)1); addBE(bytes, xs.convertDelayedResourceToResource(pr.r).getResourceId()); } else { bytes.add((byte)0); } } addBE(bytes, bean.c.length); for(PossibleResource pr : bean.c) { if(pr.r != null) { bytes.add((byte)1); addBE(bytes, xs.convertDelayedResourceToResource(pr.r).getResourceId()); } else { bytes.add((byte)0); } } return bytes.toArray(); } int readBE4(byte[] bytes, int byteIndex) { int result = (int) (((int)(bytes[byteIndex++] & 0xff))<<24) | (((int)(bytes[byteIndex++] & 0xff))<<16) | (((int)(bytes[byteIndex++] & 0xff))<<8) | (((int)(bytes[byteIndex++] & 0xff))); return result; } long readBE8(byte[] bytes, int byteIndex) { long result = (long) (((long)(bytes[byteIndex++] & 0xff))<<56) | (((long)(bytes[byteIndex++] & 0xff))<<48) | (((long)(bytes[byteIndex++] & 0xff))<<40) | (((long)(bytes[byteIndex++] & 0xff))<<32) | (((long)(bytes[byteIndex++] & 0xff))<<24) | (((long)(bytes[byteIndex++] & 0xff))<<16) | (((long)(bytes[byteIndex++] & 0xff))<<8) | (((long)(bytes[byteIndex++] & 0xff))); return result; } public Object deserialize(byte[] data) throws IOException { BTreeContentBean result = new BTreeContentBean(); try { result.leaf = (data[0] == 1) ? true : false; int byteIndex = 1; result.n = readBE4(data, byteIndex); byteIndex += 4; Binding pbv = PossibleVariant.BINDING; Serializer pbs = pbv.serializer(); // Redundant array size int keySize = readBE4(data, byteIndex); result.key = new PossibleVariant[keySize]; byteIndex += 4; for(int i=0;i 0; if(has) { result.value[i] = PossibleResource.read(ss, readBE8(data, byteIndex)); byteIndex += 8; } else { result.value[i] = new PossibleResource(); } } // Redundant array size int childSize = readBE4(data, byteIndex); result.c = new PossibleResource[childSize]; byteIndex += 4; for(int i=0;i 0; if(has) { result.c[i] = PossibleResource.read(ss, readBE8(data, byteIndex)); byteIndex += 8; } else { result.c[i] = new PossibleResource(); } } } catch (DatabaseException e) { e.printStackTrace(); } return result; } }; @Override public void accept(Visitor1 v, Object obj) { throw new UnsupportedOperationException(); } @Override public T accept(Visitor v) { throw new UnsupportedOperationException(); } @Override public boolean isInstance(Object obj) { throw new UnsupportedOperationException(); } @Override public void readFrom(Binding srcBinding, Object src, Object dst) throws BindingException { throw new UnsupportedOperationException(); } @Override public void assertInstaceIsValid(Object obj, Set validInstances) throws BindingException { throw new UnsupportedOperationException(); } @Override public int deepHashValue(Object value, IdentityHashMap hashedObjects) throws BindingException { throw new UnsupportedOperationException(); } @Override public int deepCompare(Object o1, Object o2, Set> compareHistory) throws BindingException { throw new UnsupportedOperationException(); } @Override protected void toString(Object value, BindingPrintContext ctx) throws BindingException { throw new UnsupportedOperationException(); } @Override public int getComponentCount() { throw new UnsupportedOperationException(); } @Override public Binding getComponentBinding(int index) { throw new UnsupportedOperationException(); } @Override public Binding getComponentBinding(ChildReference path) { throw new UnsupportedOperationException(); } @Override public Serializer serializer() throws RuntimeSerializerConstructionException { return serializer; } } final public class BTreeUtils implements BTreeContentManager { final private Resource tree; final private Resource ownerRelation; final public Binding CONTENT_BEAN_BINDING; final public DatatypeResource DATA; private long mod; private int t; private Map beans; private Map owners; private Resource root; private Resource nodeType; private boolean cached; public BTreeUtils(ReadGraph graph, Resource tree, int t, Resource nodeType, Resource ownerRelation, boolean cached) throws DatabaseException { try { SerialisationSupport ss = graph.getService(SerialisationSupport.class); XSupport xs = graph.getService(XSupport.class); CONTENT_BEAN_BINDING = new BTreeContentBinding(ss, xs); DATA = DatatypeResource.getInstance(graph); this.tree = tree; this.ownerRelation = ownerRelation; this.nodeType = nodeType != null ? nodeType : DATA.BTreeNode; this.t = t; Long possibleMod = graph.getPossibleRelatedValue(tree, DATA.BTree_mod, Bindings.LONG); this.mod = possibleMod != null ? possibleMod : 0; this.cached = cached; if (cached) { this.beans = new HashMap(); this.owners = new HashMap(); } } catch (RuntimeBindingConstructionException e) { Logger.defaultLogError(e); throw new DatabaseException(e); } } public static BTreeUtils create(WriteGraph graph, Resource ownerRelation, int t, boolean cached) throws DatabaseException { DatatypeResource DATA = DatatypeResource.getInstance(graph); return create(graph, DATA.BTree, DATA.BTreeNode, ownerRelation, t, cached); } public static BTreeUtils create(WriteGraph graph, Resource type, Resource nodeType, Resource ownerRelation, int t, boolean cached) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); DatatypeResource DATA = DatatypeResource.getInstance(graph); Resource tree = graph.newResource(); graph.claim(tree, L0.InstanceOf, null, type); if(ownerRelation != null) graph.claim(tree, DATA.BTree_HasOwnerRelation, DATA.BTree_HasOwnerRelation_Inverse, ownerRelation); if(nodeType != null) graph.claim(tree, DATA.BTree_HasNodeType, DATA.BTree_HasNodeType_Inverse, nodeType); graph.claimLiteral(tree, DATA.BTree_t, t, Bindings.INTEGER); if (!cached) { graph.claimLiteral(tree, DATA.BTree_mod, 0L, Bindings.LONG); } BTreeUtils utils = new BTreeUtils(graph, tree, t, nodeType, ownerRelation, cached); Resource node = utils.createNode(graph, utils, t); utils.setRoot(graph, node); utils.setOwner(graph, node, tree, true); return utils; } public Resource getTree() { return tree; } public void insert(WriteGraph graph, Variant k, Resource v) throws DatabaseException { Resource r = getRoot(graph); insertImpl(graph, this, r, t, k, v); } static class BatchContentManager implements BTreeContentManager { final private BTreeUtils bu; final Map beans = new HashMap(); public BatchContentManager(BTreeUtils bu) { this.bu = bu; } @Override public BTreeContentBean getContentBean(ReadGraph graph, Resource node) throws DatabaseException { BTreeContentBean bean = beans.get(node); if(bean != null) return bean; return bu.getContentBean(graph, node); } @Override public void setContentBean(WriteGraph graph, Resource node, BTreeContentBean bean) throws DatabaseException { beans.put(node, bean); } public void apply(WriteGraph graph) throws DatabaseException { for(Map.Entry entry : beans.entrySet()) { bu.setContentBean(graph, entry.getKey(), entry.getValue()); } } } public void insertAll(WriteGraph graph, Collection> values) throws DatabaseException { Resource r = getRoot(graph); BatchContentManager cm = new BatchContentManager(this); for(Pair entry : values) { insertImpl(graph, cm, r, t, entry.first, entry.second); } cm.apply(graph); } public Resource search(ReadGraph graph, Variant k) throws DatabaseException { Resource r = getRoot(graph); return searchNode(graph, r, k); } // Implementation private int findKey(ReadGraph graph, BTreeContentManager manager, BTreeContentBean bean, Variant k) throws DatabaseException { int idx = 0; while(idx bean.n) return removeImpl(graph, manager, t, bean.c[idx-1].r, manager.getContentBean(graph, bean.c[idx-1].r), k); else return removeImpl(graph, manager, t, bean.c[idx].r, cContent, k); } } // A function to get predecessor of keys[idx] Pair getPred(ReadGraph graph, BTreeContentManager manager, BTreeContentBean bean, int idx) throws DatabaseException { // Keep moving to the right most node until we reach a leaf bean = manager.getContentBean(graph, bean.c[idx].r); while (!bean.leaf) bean = manager.getContentBean(graph, bean.c[bean.n].r); // Return the last key of the leaf return new Pair(bean.key[bean.n-1].v, bean.value[bean.n-1].r); } Pair getSucc(ReadGraph graph, BTreeContentManager manager, BTreeContentBean bean, int idx) throws DatabaseException { // Keep moving the left most node starting from C[idx+1] until we reach a leaf bean = manager.getContentBean(graph, bean.c[idx+1].r); while (!bean.leaf) bean = manager.getContentBean(graph, bean.c[0].r); // Return the first key of the leaf return new Pair(bean.key[0].v, bean.value[0].r); } // A function to fill child C[idx] which has less than t-1 keys void fill(WriteGraph graph, BTreeContentManager manager, Resource r, BTreeContentBean bean, int t, int idx) throws DatabaseException { // If the previous child(C[idx-1]) has more than t-1 keys, borrow a key // from that child if (idx!=0) { BTreeContentBean cContent = manager.getContentBean(graph, bean.c[idx-1].r); if (cContent.n>=t) { borrowFromPrev(graph, manager, r, bean, idx); return; } } // If the next child(C[idx+1]) has more than t-1 keys, borrow a key // from that child if (idx!=bean.n) { BTreeContentBean cContent = manager.getContentBean(graph, bean.c[idx+1].r); if (cContent.n>=t) { borrowFromNext(graph, manager, r, bean, idx); return; } } // Merge C[idx] with its sibling // If C[idx] is the last child, merge it with with its previous sibling // Otherwise merge it with its next sibling if (idx != bean.n) merge(graph, manager, r, bean, t, idx); else merge(graph, manager, r, bean, t, idx-1); } // A function to borrow a key from C[idx-1] and insert it // into C[idx] void borrowFromPrev(WriteGraph graph, BTreeContentManager manager, Resource r, BTreeContentBean bean, int idx) throws DatabaseException { BTreeContentBean childContent = manager.getContentBean(graph, bean.c[idx].r); BTreeContentBean siblingContent = manager.getContentBean(graph, bean.c[idx-1].r); // The last key from C[idx-1] goes up to the parent and key[idx-1] // from parent is inserted as the first key in C[idx]. Thus, the loses // sibling one key and child gains one key // Moving all key in C[idx] one step ahead for (int i=childContent.n-1; i>=0; --i) { childContent.key[i+1].v = childContent.key[i].v; childContent.value[i+1].r = childContent.value[i].r; } // If C[idx] is not a leaf, move all its child pointers one step ahead if (!childContent.leaf) { for(int i=childContent.n; i>=0; --i) { childContent.c[i+1].r = childContent.c[i].r; setOwner(graph, childContent.c[i+1].r, bean.c[idx].r, true); } } // Setting child's first key equal to keys[idx-1] from the current node childContent.key[0].v = bean.key[idx-1].v; childContent.value[0].r = bean.value[idx-1].r; // Moving sibling's last child as C[idx]'s first child // TODO: is this correct? if (!bean.leaf) { Resource lastChild = siblingContent.c[siblingContent.n].r; if (lastChild != null) { childContent.c[0].r = lastChild; setOwner(graph, childContent.c[0].r, bean.c[idx].r, true); } } // Moving the key from the sibling to the parent // This reduces the number of keys in the sibling bean.key[idx-1].v = siblingContent.key[siblingContent.n-1].v; bean.value[idx-1].r = siblingContent.value[siblingContent.n-1].r; childContent.n += 1; siblingContent.reduceSize(); //siblingContent.n -= 1; manager.setContentBean(graph, r, bean); manager.setContentBean(graph, bean.c[idx].r, childContent); manager.setContentBean(graph, bean.c[idx-1].r, siblingContent); } // A function to borrow a key from the C[idx+1] and place // it in C[idx] void borrowFromNext(WriteGraph graph, BTreeContentManager manager, Resource r, BTreeContentBean bean, int idx) throws DatabaseException { BTreeContentBean childContent = manager.getContentBean(graph, bean.c[idx].r); BTreeContentBean siblingContent = manager.getContentBean(graph, bean.c[idx+1].r); // keys[idx] is inserted as the last key in C[idx] childContent.key[childContent.n].v = bean.key[idx].v; childContent.value[childContent.n].r = bean.value[idx].r; // Sibling's first child is inserted as the last child // into C[idx] if (!childContent.leaf) { childContent.c[childContent.n+1].r = siblingContent.c[0].r; setOwner(graph, childContent.c[childContent.n+1].r, bean.c[idx].r, true); } //The first key from sibling is inserted into keys[idx] bean.key[idx].v = siblingContent.key[0].v; bean.value[idx].r = siblingContent.value[0].r; // Moving all keys in sibling one step behind for (int i=1; i= t) { Pair pred = getPred(graph, manager, bean, idx); bean.key[idx].v = pred.first; bean.value[idx].r = pred.second; removeImpl(graph, manager, t, bean.c[idx].r, cContent, pred.first); manager.setContentBean(graph, r, bean); } else { BTreeContentBean cContentPlus1 = manager.getContentBean(graph, bean.c[idx+1].r); // If the child C[idx] has less that t keys, examine C[idx+1]. // If C[idx+1] has atleast t keys, find the successor 'succ' of k in // the subtree rooted at C[idx+1] // Replace k by succ // Recursively delete succ in C[idx+1] if (cContentPlus1.n >= t) { Pair succ = getSucc(graph, manager, bean, idx); bean.key[idx].v = succ.first; bean.value[idx].r = succ.second; removeImpl(graph, manager, t, bean.c[idx+1].r, cContentPlus1, succ.first); manager.setContentBean(graph, r, bean); } // If both C[idx] and C[idx+1] has less that t keys,merge k and all of C[idx+1] // into C[idx] // Now C[idx] contains 2t-1 keys // Free C[idx+1] and recursively delete k from C[idx] else { merge(graph, manager, r, bean, t, idx); removeImpl(graph, manager, t, bean.c[idx].r, cContent, k); } } } private Resource createNode(WriteGraph graph, BTreeContentManager manager, int t) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); Resource result = graph.newResource(); mod++; graph.claim(result, L0.InstanceOf, null, nodeType); if (!cached) { graph.claimLiteral(tree, DATA.BTree_mod, mod, Bindings.LONG); } graph.addLiteral(result, L0.HasName, L0.NameOf, ""+mod, Bindings.STRING); manager.setContentBean(graph, result, BTreeContentBean.create(t)); return result; } public Resource searchNode(ReadGraph graph, Resource x, Variant k) throws DatabaseException { BTreeContentBean xContent = getContentBean(graph, x); int i=1; while(i <= xContent.n && k.compareTo(xContent.key[i-1].v) > 0) i++; if(i <= xContent.n && k.equals(xContent.key[i-1].v)) return xContent.value[i-1].r; if(xContent.leaf) return null; else return searchNode(graph, xContent.c[i-1].r, k); } private void splitChild(WriteGraph graph, BTreeContentManager manager, int t, Resource x, int i, Resource y) throws DatabaseException { Resource z = allocateNode(graph, manager, t); BTreeContentBean xContent = manager.getContentBean(graph, x); BTreeContentBean yContent = manager.getContentBean(graph, y); BTreeContentBean zContent = manager.getContentBean(graph, z); zContent.leaf = yContent.leaf; zContent.n = t-1; for(int j=1;j<=t-1;j++) { zContent.key[j-1].v = yContent.key[j+t-1].v; zContent.value[j-1].r = yContent.value[j+t-1].r; setOwner(graph, zContent.value[j-1].r, z, false); } if(!yContent.leaf) { for(int j=1;j<=t;j++) { zContent.c[j-1].r = yContent.c[j+t-1].r; setOwner(graph, zContent.c[j-1].r, z, true); } } //yContent.n = t-1; for(int j=xContent.n+1;j>=i+1;j--) { xContent.c[j+1-1].r = xContent.c[j-1].r; } xContent.c[i+1-1].r = z; setOwner(graph, z, x, true); for(int j=xContent.n;j>=i;j--) { xContent.key[j+1-1].v = xContent.key[j-1].v; xContent.value[j+1-1].r = xContent.value[j-1].r; } xContent.key[i-1].v = yContent.key[t-1].v; xContent.value[i-1].r = yContent.value[t-1].r; xContent.n++; while (yContent.n > t-1) yContent.reduceSize(); setOwner(graph, xContent.value[i-1].r, x, true); manager.setContentBean(graph, x, xContent); manager.setContentBean(graph, y, yContent); manager.setContentBean(graph, z, zContent); } private void insertNonfull(WriteGraph graph, BTreeContentManager manager, int t, Resource x, Variant k, Resource v) throws DatabaseException { BTreeContentBean xContent = manager.getContentBean(graph, x); int i = xContent.n; if(xContent.leaf) { while(i >= 1 && k.compareTo(xContent.key[i-1].v) < 0) { xContent.key[i+1-1].v = xContent.key[i-1].v; xContent.value[i+1-1].r = xContent.value[i-1].r; i--; } xContent.key[i+1-1].v = k; xContent.value[i+1-1].r = v; xContent.n++; setOwner(graph, v, x, false); manager.setContentBean(graph, x, xContent); } else { while(i >= 1 && k.compareTo(xContent.key[i-1].v) < 0) i--; i++; BTreeContentBean xciContent = manager.getContentBean(graph, xContent.c[i-1].r); if(xciContent.n == 2*t-1) { splitChild(graph, manager, t, x, i, xContent.c[i-1].r); xContent = manager.getContentBean(graph, x); if(k.compareTo(xContent.key[i-1].v) > 0) i++; } insertNonfull(graph, manager, t, xContent.c[i-1].r, k, v); } } private void setOwner(WriteGraph graph, Resource r, Resource owner, boolean force) throws DatabaseException { if (r != null) { if((ownerRelation != null) || force) { Resource own = ownerRelation != null ? ownerRelation : DATA.BTreeNode_IsOwnedBy; if (cached) { owners.put(r, owner); } else { graph.deny(r, own); graph.claim(r, own, owner); } } } } private Resource getRoot(ReadGraph graph) throws DatabaseException { if (cached) { if (root != null) return root; } return graph.getPossibleObject(tree, DATA.BTree_root); } private void setRoot(WriteGraph graph, Resource r) throws DatabaseException { if (cached) { this.root = r; } else { graph.deny(tree, DATA.BTree_root); graph.claim(tree, DATA.BTree_root, r); } } private Resource allocateNode(WriteGraph graph, BTreeContentManager manager, int t) throws DatabaseException { return createNode(graph, manager, t); } public void setContentBean(WriteGraph graph, Resource node, BTreeContentBean bean) throws DatabaseException { if (cached) { beans.put(node, bean); } else { graph.claimLiteral(node, DATA.BTreeNode_content, DATA.BTreeNode_Content, bean, CONTENT_BEAN_BINDING ); } } public void flushCache(WriteGraph graph) throws DatabaseException { assert(cached); XSupport xs = graph.getService(XSupport.class); for(Map.Entry entry : beans.entrySet()) { Resource node = xs.convertDelayedResourceToResource(entry.getKey()); if (node != null) { graph.claimLiteral(node, DATA.BTreeNode_content, DATA.BTreeNode_Content, entry.getValue(), CONTENT_BEAN_BINDING ); } } for(Map.Entry entry : owners.entrySet()) { Resource r = xs.convertDelayedResourceToResource(entry.getKey()); Resource owner = xs.convertDelayedResourceToResource(entry.getValue()); graph.deny(r, ownerRelation); if (owner != null) { graph.claim(r, ownerRelation, owner); } } Resource tree2 = xs.convertDelayedResourceToResource(tree); graph.claimLiteral(tree2, DATA.BTree_mod, mod, Bindings.LONG); if (root != null) { Resource root2 = xs.convertDelayedResourceToResource(root); graph.deny(tree2, DATA.BTree_root); graph.claim(tree2, DATA.BTree_root, root2); } } public BTreeContentBean getContentBean(ReadGraph graph, Resource node) throws DatabaseException { if (cached) { BTreeContentBean bean = beans.get(node); if (bean != null) return bean; } return graph.syncRequest(new RelatedValue(node, DATA.BTreeNode_content, CONTENT_BEAN_BINDING), TransientCacheListener.instance()); } public List entries(ReadGraph graph) throws DatabaseException { Resource r = getRoot(graph); List results = new ArrayList(); return collectEntries(graph, r, null, null, results); } private List collectEntries(ReadGraph graph, Resource x, Variant min, Variant max, List results) throws DatabaseException { BTreeContentBean xContent = getContentBean(graph, x); for (int i = 0; i < xContent.n + 1; i++) { if (min != null && i < xContent.n && min.compareTo(xContent.key[i].v) > 0) continue; if (!xContent.leaf) { Resource child = xContent.c[i].r; if (child != null) { // reduce the amount of comparison if we know that the range of the child keys matches Variant min2 = (min != null && i > 0 && min.compareTo(xContent.key[i - 1].v) < 0) ? null : min; Variant max2 = (max != null && i < xContent.n - 1 && max.compareTo(xContent.key[i + 1].v) > 0) ? null : max; collectEntries(graph, child, min2, max2, results); } } if (i < xContent.n) { if (max != null && max.compareTo(xContent.key[i].v) < 0) return results; results.add(new Tuple2(xContent.key[i].v, xContent.value[i].r)); } } return results; } public List searchRange(ReadGraph graph, Variant min, Variant max) throws DatabaseException { Resource r = getRoot(graph); List results = new ArrayList(); return collectEntries(graph, r, min, max, results); } }