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