]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryMap.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / binary / BinaryMap.java
diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryMap.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryMap.java
new file mode 100644 (file)
index 0000000..0915acf
--- /dev/null
@@ -0,0 +1,1929 @@
+/*******************************************************************************\r
+ *  Copyright (c) 2010 Association for Decentralized Information Management in\r
+ *  Industry THTH ry.\r
+ *  All rights reserved. This program and the accompanying materials\r
+ *  are made available under the terms of the Eclipse Public License v1.0\r
+ *  which accompanies this distribution, and is available at\r
+ *  http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ *  Contributors:\r
+ *      VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.accessor.binary;
+
+import java.io.IOException;\r
+import java.lang.ref.SoftReference;\r
+import java.util.Map;\r
+import java.util.TreeMap;\r
+import java.util.concurrent.Executor;\r
+\r
+import org.simantics.databoard.accessor.Accessor;\r
+import org.simantics.databoard.accessor.MapAccessor;\r
+import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
+import org.simantics.databoard.accessor.error.AccessorException;\r
+import org.simantics.databoard.accessor.error.ReferenceException;\r
+import org.simantics.databoard.accessor.event.Event;\r
+import org.simantics.databoard.accessor.event.MapEntryAdded;\r
+import org.simantics.databoard.accessor.event.MapEntryRemoved;\r
+import org.simantics.databoard.accessor.event.ValueAssigned;\r
+import org.simantics.databoard.accessor.file.FileMapAccessor;\r
+import org.simantics.databoard.accessor.impl.AccessorParams;\r
+import org.simantics.databoard.accessor.impl.ListenerEntry;\r
+import org.simantics.databoard.accessor.interestset.InterestSet;\r
+import org.simantics.databoard.accessor.interestset.MapInterestSet;\r
+import org.simantics.databoard.accessor.reference.ChildReference;\r
+import org.simantics.databoard.accessor.reference.KeyReference;\r
+import org.simantics.databoard.accessor.reference.LabelReference;\r
+import org.simantics.databoard.adapter.AdaptException;\r
+import org.simantics.databoard.adapter.Adapter;\r
+import org.simantics.databoard.adapter.AdapterConstructionException;\r
+import org.simantics.databoard.binding.ArrayBinding;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.MapBinding;\r
+import org.simantics.databoard.binding.error.BindingConstructionException;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.mutable.MutableVariant;\r
+import org.simantics.databoard.parser.repository.DataTypeSyntaxError;\r
+import org.simantics.databoard.parser.repository.DataValueRepository;\r
+import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;\r
+import org.simantics.databoard.serialization.SerializationException;\r
+import org.simantics.databoard.serialization.Serializer;\r
+import org.simantics.databoard.serialization.SerializerConstructionException;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.databoard.type.MapType;\r
+import org.simantics.databoard.util.binary.Blob;\r
+import org.simantics.databoard.util.binary.RandomAccessBinary.ByteSide;\r
+
+/**
+ * BinaryMap is accessor to a map structure in a file or byte memory.
+ * <p> (Fixed sized entries)
+ * If the size of an entry is constant, then the implementation uses binary 
+ * search for random access operations O(log(n)). 
+ * 
+ * <p> (Variable sized entries)
+ * If the size is variable, for example the key or value is string, then the 
+ * implementation does sequential search for all operations, which is very 
+ * slow O(n). To improve performance, an index is built in memory as the file
+ * is read. To conserve memory, the index is held with SoftReference. So, 
+ * the performance remains good if lots of operations are done within a short
+ * time period.
+ * 
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public class BinaryMap extends BinaryObject implements MapAccessor, FileMapAccessor {
+
+       /** Accessors to children */
+       TreeMap<Object, java.lang.ref.Reference<BinaryObject>> children;
+       
+       Binding kb;
+       Binding vb;
+       Serializer ks;
+       Serializer vs;
+       
+       /** Entry size, constant */
+       Integer constantSize;
+       
+       Index index;
+       boolean indexing = false;
+       
+       public BinaryMap(BinaryObject parent, Blob blob, Datatype type, AccessorParams params) {
+               super(parent, blob, type, params);
+               MapType mt = (MapType) type;
+               kb = params.bindingScheme.getBindingUnchecked( mt.keyType );
+               vb = params.bindingScheme.getBindingUnchecked( mt.valueType );
+               ks = params.serializerScheme.getSerializerUnchecked( kb );
+               vs = params.serializerScheme.getSerializerUnchecked( vb );
+               children = new TreeMap<Object, java.lang.ref.Reference<BinaryObject>>(kb);
+               
+               if (ks.getConstantSize()!=null && vs.getConstantSize()!=null)  {
+                       constantSize = ks.getConstantSize() + vs.getConstantSize();
+                       index = new Constant();
+               } else {
+                       index = new Sequential();
+               }
+       } 
+       
+       // Get existing accessor
+       BinaryObject getChild(Object key)
+       {
+               java.lang.ref.Reference<BinaryObject> ref = children.get(key);
+               if (ref==null) return null;
+               BinaryObject result = ref.get();
+               if (result==null) {
+                       children.remove(key);
+                       return null;
+               }
+               return result;
+       }               
+       
+       Entry floorChildEntry(Object key) throws IOException, SerializationException, BindingException {
+               if (ks.getConstantSize()==null) return null;
+               BinaryObject sa = getChild(key);
+               // Exact match
+               if (sa!=null) {
+                       if (ks.getConstantSize()==null) return null;
+                       long valuePos = sa.b.getStartPositionInSourceBinary();
+                       long keyPos = valuePos - ks.getConstantSize();
+                       return new Entry(key, keyPos, sa);
+               }
+               return lowerChildEntry(key);
+       }
+
+       Entry lowerChildEntry(Object key) throws IOException, SerializationException, BindingException {
+               if (ks.getConstantSize()==null) return null;
+               Map.Entry<Object, java.lang.ref.Reference<BinaryObject>> e = children.lowerEntry(key);
+               while (e != null) {
+                       key = e.getKey();
+                       BinaryObject sa = e.getValue().get();
+                       if (sa!=null) {
+                               long valuePos = sa.b.getStartPositionInSourceBinary();
+                               long keyPos = valuePos - ks.getConstantSize();
+                               return new Entry(key, keyPos, sa);
+                       } else {
+                               children.remove(e.getKey());
+                       }
+                       e = children.lowerEntry(key);
+               }
+               return null;
+       }
+
+       Entry lastChildEntry() throws IOException, SerializationException, BindingException {
+               if (ks.getConstantSize()==null) return null;
+               Map.Entry<Object, java.lang.ref.Reference<BinaryObject>> e = children.lastEntry();
+               while (e != null) {
+                       Object key = e.getKey();
+                       BinaryObject sa = e.getValue().get();
+                       if (sa!=null) {
+                               long valuePos = sa.b.getStartPositionInSourceBinary();
+                               long keyPos = valuePos - ks.getConstantSize();
+                               return new Entry(key, keyPos, sa);
+                       } else {
+                               children.remove(key);
+                       }
+                       e = children.lowerEntry(key);
+               }
+               return null;
+       }
+       
+       Entry higherChildEntry(Object key) throws IOException, SerializationException, BindingException {
+               if (ks.getConstantSize()==null) return null;
+               Map.Entry<Object, java.lang.ref.Reference<BinaryObject>> e = children.higherEntry(key);
+               while (e != null) {
+                       key = e.getKey();
+                       BinaryObject sa = e.getValue().get();
+                       if (sa!=null) {
+                               long valuePos = sa.b.getStartPositionInSourceBinary();
+                               long keyPos = valuePos - ks.getConstantSize();
+                               return new Entry(key, keyPos, sa);
+                       } else {
+                               children.remove(key);
+                       }
+                       e = children.higherEntry(key);
+               }
+               return null;
+       }
+
+       Entry ceilingChildEntry(Object key) throws IOException, SerializationException, BindingException {
+               if (ks.getConstantSize()==null) return null;
+               BinaryObject sa = getChild(key);
+               // Exact match
+               if (sa!=null) {
+                       long valuePos = sa.b.getStartPositionInSourceBinary();
+                       long keyPos = valuePos - ks.getConstantSize();
+                       return new Entry(key, keyPos, sa);
+               }
+               return higherChildEntry(key);
+       }
+       
+       Entry getEntryAt(long pos) throws SerializationException, IOException, BindingException {\r
+               Object k = getKeyAt(pos);
+               if (k==null) return null;
+               return new Entry(k, pos, getChild(k));
+       }
+       
+       Object getKeyAt(long pos) throws SerializationException, IOException, BindingException {
+               if (pos<4L) return null;
+               if (pos>=b.length()) return null;
+               b.position(pos);
+               Object k = ks.deserialize(b, null);
+               return k;
+       }       
+       
+       @Override
+       public MapType type() {
+               return (MapType) type;
+       }
+
+       @Override
+       public void clear() throws AccessorException {\r
+               assert b.isOpen();\r
+               writeLock();
+               try {
+                       clearNoflush();
+                       b.flush();
+               } catch (IOException e) {
+                       throw new AccessorException( e );
+               } finally {\r
+                       writeUnlock();\r
+               }
+       }       
+
+       @Override
+       public int size() throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       if (constantSize != null) return (int) ((b.length()-4) / constantSize);
+                       b.position(0L);
+                       return b.readInt();
+               } catch (IOException e) {
+                       throw new AccessorException( e );
+               } finally {\r
+                       readUnlock();\r
+               }
+       }       
+
+       @Override
+       public boolean containsKey(Binding keyBinding, Object key)
+                       throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       Object lk = adapt(key, keyBinding, kb);
+                       Entry e = index.getKey(lk);
+                       return e != null;
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               } catch (AdapterConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+
+       @Override
+       public boolean containsValue(Binding valueBinding, Object value)
+                       throws AccessorException {              \r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       Serializer vs = params.serializerScheme.getSerializer( valueBinding );
+                       b.position(0L);
+                       int count = b.readInt();
+                       for (int i=0; i<count; i++) {
+                               ks.skip(b);
+                               Object v = vs.deserialize(b, null);
+                               if (valueBinding.equals(v, value)) return true;
+                       }
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } catch (SerializerConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } finally {\r
+                       readUnlock();\r
+               }
+               return false;
+       }
+
+       @Override
+       public Object get(Binding keyBinding, Object key, Binding valueBinding)
+                       throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       Object lk = adapt(key, keyBinding, kb);
+                       Entry e = index.getKey(lk);
+                       if (e==null) return null;
+                       b.position(e.pos);
+                       ks.skip(b, null);
+                       Object value = params.serializerScheme.getSerializer( valueBinding ).deserialize(b, null);                      
+                       return value;
+               } catch (AdaptException e1) {
+                       throw new AccessorException(e1);
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } catch (RuntimeSerializerConstructionException e) {
+                       throw new AccessorException(e);
+               } catch (AdapterConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } catch (SerializerConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+
+       @Override
+       public void getAll(Binding keyBinding, Binding valueBinding,
+                       Map<Object, Object> to) throws AccessorException {
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       Adapter ka = params.adapterScheme.getAdapter(kb, keyBinding, true, false);
+                       Adapter va = params.adapterScheme.getAdapter(vb, valueBinding, true, false);
+                       b.position(0L);
+                       int count = b.readInt();
+                       for (int i=0; i<count; i++) {
+                               Object k = ks.deserialize(b, null);
+                               Object v = vs.deserialize(b, null);
+                               k = ka.adapt(k);
+                               v = va.adapt(v);
+                               to.put(k, v);
+                       }                       
+                       
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } catch (AdapterConstructionException e) {
+                       throw new AccessorException(e);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               } finally {\r
+                       readUnlock();\r
+               }
+               
+       }
+
+       @Override
+       public void getAll(Binding keyBinding, Binding valueBinding, Object[] keys,
+                       Object[] values) throws AccessorException {
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       Adapter ka = params.adapterScheme.getAdapter(kb, keyBinding, true, false);
+                       Adapter va = params.adapterScheme.getAdapter(vb, valueBinding, true, false);
+                       b.position(0L);
+                       int count = b.readInt();
+                       if (count>keys.length) throw new AccessorException("keys array too short");
+                       if (count>values.length) throw new AccessorException("values array too short");
+                       for (int i=0; i<count; i++) {
+                               Object k = ks.deserialize(b, null);
+                               Object v = vs.deserialize(b, null);
+                               k = ka.adapt(k);
+                               v = va.adapt(v);
+                               keys[i] = k;
+                               values[i] = v;
+                       }                       
+                       
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } catch (AdapterConstructionException e) {
+                       throw new AccessorException(e);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               } finally {\r
+                       readUnlock();\r
+               }
+               
+       }\r
+       \r
+       @Override\r
+       public int count(Binding keyBinding, Object from, boolean fromInclusive,\r
+                       Object end, boolean endInclusive) throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();\r
+               try {\r
+                       Object lf = params.adapterScheme.adapt(from, keyBinding, kb);\r
+                       Object le = params.adapterScheme.adapt(end, keyBinding, kb);\r
+                       \r
+                       Entry fromEntry = fromInclusive ? index.ceiling(lf) : index.higher(lf);\r
+                       Entry endEntry  = endInclusive  ? index.floor(le)   : index.lower(le);\r
+                       if (endEntry==null || fromEntry == null) return 0;\r
+\r
+                       if (fromEntry.pos>endEntry.pos) return 0;\r
+                       if (fromEntry.pos==endEntry.pos) return 1;\r
+                                               \r
+                       if (constantSize != null) {\r
+                               return (int) ((endEntry.pos-fromEntry.pos)/constantSize)+1;\r
+                       }\r
+                       \r
+                       int result = 1;\r
+                       b.position(fromEntry.pos);\r
+                       while (b.position()<endEntry.pos) {\r
+                               ks.skip(b);\r
+                               vs.skip(b);\r
+                               result++;\r
+                       }\r
+                       return result;                  \r
+               } catch (IOException e) {\r
+                       throw new AccessorException(e);\r
+               } catch (AdaptException e) {\r
+                       throw new AccessorException(e);\r
+               } finally {\r
+                       readUnlock();\r
+               }\r
+       }\r
+       \r
+       @Override\r
+       public int getEntries(Binding keyBinding, Object from,\r
+                       boolean fromInclusive, Object end, boolean endInclusive,\r
+                       ArrayBinding keyArrayBinding, Object keysArray,\r
+                       ArrayBinding valueArrayBinding, Object valueArray, int limit)\r
+                       throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();\r
+               try {\r
+                       Object lf = params.adapterScheme.adapt(from, keyBinding, kb);\r
+                       Object le = params.adapterScheme.adapt(end, keyBinding, kb);\r
+                       \r
+                       Entry fromEntry = fromInclusive ? index.ceiling(lf) : index.higher(lf);\r
+                       Entry endEntry  = endInclusive  ? index.floor(le)   : index.lower(le);\r
+                       if (endEntry==null || fromEntry == null) return 0;\r
+                       \r
+                       // Requester's Key & Value Binding\r
+                       Binding rkb   = keyArrayBinding.getComponentBinding();\r
+                       Binding rvb   = valueArrayBinding.getComponentBinding();\r
+                       \r
+                       // Local Key & Value type & bindings\r
+                       Datatype lkt  = type().keyType;\r
+                       Datatype lvt  = type().valueType;\r
+                       \r
+                       boolean adaptKey   = !rkb.type().equals(lkt);\r
+                       boolean adaptValue = !rvb.type().equals(lvt);\r
+                       \r
+                       Serializer ks, vs;\r
+                       Adapter ka = null, va = null;\r
+                       \r
+                       if (adaptKey) {\r
+                               Binding lkb   = params.bindingScheme.getBinding( lkt );\r
+                               ka = params.adapterScheme.getAdapter( lkb, rkb, true, false);\r
+                               ks = params.serializerScheme.getSerializer( lkb );\r
+                       } else {\r
+                               ks = params.serializerScheme.getSerializer( rkb );                              \r
+                       }\r
+                       \r
+                       if (adaptValue) {\r
+                               Binding lvb   = params.bindingScheme.getBinding( lvt );\r
+                               va = params.adapterScheme.getAdapter( lvb, rvb, true, false);\r
+                               vs = params.serializerScheme.getSerializer( lvb );\r
+                       } else {\r
+                               vs = params.serializerScheme.getSerializer( rvb );\r
+                       }                       \r
+                       \r
+                       int i = 0;\r
+                       int kac = keyArrayBinding.size( keysArray );\r
+                       int vac = valueArrayBinding.size( valueArray ); \r
+                       b.position(fromEntry.pos);\r
+                       while (b.position()<=endEntry.pos) {\r
+                               if (limit>=0 && i>=limit) break;\r
+                               Object key = ks.deserialize(b);\r
+                               if (adaptKey) key = ka.adapt(key);\r
+                               Object value = vs.deserialize(b);\r
+                               if (adaptValue) value = va.adapt(value);\r
+                               \r
+                               if (i<kac) keyArrayBinding.set(keysArray, i, key); else keyArrayBinding.add(keysArray, i, key);\r
+                               if (i<vac) valueArrayBinding.set(valueArray, i, value); else valueArrayBinding.add(valueArray, i, value);\r
+                               \r
+                               i++;\r
+                       }\r
+                       return i;                       \r
+               } catch (IOException e) {\r
+                       throw new AccessorException(e);\r
+               } catch (AdaptException e) {\r
+                       throw new AccessorException(e);\r
+               } catch (SerializerConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } catch (BindingException e) {\r
+                       throw new AccessorException(e);\r
+               } catch (BindingConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } catch (AdapterConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } finally {\r
+                       readUnlock();\r
+               }\r
+       }       
+
+       @Override
+       public Object getCeilingKey(Binding keyBinding, Object key)
+                       throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       Object lk = adapt(key, keyBinding, kb);
+                       if (ks.getConstantSize()!=null) {\r
+                               if (getChild(lk)!=null) return key;\r
+                       }
+                       Entry e = index.ceiling(lk);
+                       if (e==null) return null;
+                       return adapt(e.key, kb, keyBinding);
+               } catch (AdaptException e1) {
+                       throw new AccessorException( e1 );
+               } catch (AdapterConstructionException e) {\r
+                       throw new AccessorException( e );\r
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+
+       @Override
+       public Object getFirstKey(Binding keyBinding) throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       b.position(0L);
+                       int count = b.readInt();
+                       if (count==0) return null;
+                       return params.serializerScheme.getSerializer( keyBinding ).deserialize(b);
+               } catch (RuntimeSerializerConstructionException e) {
+                       throw new AccessorException(e);
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } catch (SerializerConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+
+       @Override
+       public Object getFloorKey(Binding keyBinding, Object key)
+                       throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       Object lk = adapt(key, keyBinding, kb);
+                       if (ks.getConstantSize()!=null) {\r
+                               // See if we get exact match
+                               if (getChild(lk)!=null) return key;
+                       }
+                       Entry e = index.floor(lk);
+                       if (e==null) return null;
+                       return adapt(e.key, kb, keyBinding);
+               } catch (AdaptException e1) {
+                       throw new AccessorException( e1 );
+               } catch (AdapterConstructionException e) {\r
+                       throw new AccessorException( e );\r
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+
+       @Override
+       public Object getHigherKey(Binding keyBinding, Object key)
+                       throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       Object lk = adapt(key, keyBinding, kb);
+                       Entry e = index.higher(lk);
+                       if (e==null) return null;
+                       return adapt(e.key, kb, keyBinding);
+               } catch (AdaptException e1) {
+                       throw new AccessorException( e1 );
+               } catch (AdapterConstructionException e) {\r
+                       throw new AccessorException( e );\r
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+
+       @Override
+       public Object[] getKeys(Binding keyBinding) throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       Adapter ka = params.adapterScheme.getAdapter(kb, keyBinding, true, false);
+                       b.position(0L);
+                       int count = b.readInt();
+                       Object[] result = new Object[ count ];
+                       for (int i=0; i<count; i++) {
+                               Object k = ks.deserialize(b, null);
+                               vs.skip(b, null);
+                               k = ka.adapt(k);
+                               result[i] = k;
+                       }                       
+                       return result;                  
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } catch (AdapterConstructionException e) {
+                       throw new AccessorException(e);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+
+       @Override
+       public Object getLastKey(Binding keyBinding) throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       Entry e = index.last();
+                       if (e==null) return null;
+                       return adapt(e.key, kb, keyBinding);
+               } catch (AdaptException e1) {
+                       throw new AccessorException( e1 );
+               } catch (AdapterConstructionException e) {\r
+                       throw new AccessorException( e );\r
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+
+       @Override
+       public Object getLowerKey(Binding keyBinding, Object key)
+                       throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       Object lk = adapt(key, keyBinding, kb);
+                       Entry e = index.lower(lk);
+                       if (e==null) return null;
+                       return adapt(e.key, kb, keyBinding);
+               } catch (AdaptException e1) {
+                       throw new AccessorException( e1 );
+               } catch (AdapterConstructionException e) {\r
+                       throw new AccessorException( e );\r
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+
+
+       @SuppressWarnings("unchecked")
+       @Override
+       public <T extends Accessor> T getComponent(ChildReference reference)
+                       throws AccessorConstructionException {
+               if (reference==null) return (T) this;\r
+               \r
+               if (reference instanceof LabelReference) {\r
+                       LabelReference lr = (LabelReference) reference;\r
+                       try {\r
+                               DataValueRepository rep = new DataValueRepository(); \r
+                               kb.parseValue(lr.label, rep);\r
+                               Object value = rep.get( rep.getValueNames().iterator().next() );\r
+                               \r
+                               Accessor result = (T) getValueAccessor(kb, value);\r
+                               if (reference.getChildReference() != null)\r
+                                       result = result.getComponent(reference.getChildReference());\r
+                               return (T) result;                      \r
+                       } catch ( BindingException e1 ) {\r
+                               throw new ReferenceException(e1);                               \r
+                       } catch ( DataTypeSyntaxError e2 ) {\r
+                               throw new ReferenceException(e2);                               \r
+                       }                       \r
+               } else          
+               if (reference instanceof KeyReference) {
+                       KeyReference ref = (KeyReference) reference;
+                       Accessor result = getValueAccessor(ref.key.getBinding(), ref.key.getValue());
+                       if (reference.getChildReference() != null)
+                               result = result.getComponent(reference.getChildReference());
+                       return (T) result;                      
+               } throw new ReferenceException(reference.getClass().getName()+" is not a reference of a map");  
+       }
+       
+       @SuppressWarnings("unchecked")
+       @Override
+       public <T extends Accessor> T getValueAccessor(Binding keyBinding,
+                       Object key) throws AccessorConstructionException {
+               \r
+               assert b.isOpen();\r
+               readLock();
+               try {                   
+                       Object rk = key;
+                       Object lk = params.adapterScheme.getAdapter(keyBinding, kb, true, true).adapt(rk);
+                       
+                       BinaryObject sa = getChild(lk);
+                       if (sa!=null) return (T) sa;
+                       
+                       Entry e = index.getKey(lk);
+                       if (e == null) throw new AccessorConstructionException("Map doesn't contain the requested element");
+                       
+                       long valuePos;
+                       if (ks.getConstantSize()!=null) {
+                               valuePos = e.pos + ks.getConstantSize();
+                       } else {
+                               b.position(e.pos);
+                               ks.skip(b, null);
+                               valuePos = b.position();
+                       }
+                       
+                       long valueSize;
+                       if (vs.getConstantSize()!=null) {
+                               valueSize = vs.getConstantSize();
+                       } else {
+                               b.position(valuePos);
+                               vs.skip(b, null);
+                               valueSize = b.position() - valuePos;
+                       }
+                       
+                       // Instantiate correct sub accessor. 
+                       sa = createSubAccessor(vb.type(), valuePos, valueSize, params);
+                       children.put(lk, new SoftReference<BinaryObject>(sa));
+                               
+                       // Add component interest sets
+                       if (listeners!=null) {
+                               MutableVariant kv = new MutableVariant(kb, lk);
+                               ListenerEntry le = listeners;
+                               while (le!=null) {                              
+                                       MapInterestSet is = le.getInterestSet();
+       
+                                       // Generic element interest
+                                       InterestSet gis = is.getComponentInterest(); 
+                                       if (gis != null) {
+                                               try {
+                                                       ChildReference childPath = ChildReference.concatenate(le.path, new KeyReference(kv) );
+                                                       sa.addListener(le.listener, gis, childPath, le.executor);
+                                               } catch (AccessorException e1) {
+                                                       throw new AccessorConstructionException(e1);
+                                               }
+                                       }
+                                               
+                                       // Specific element interest
+                                       InterestSet cis = is.getComponentInterest(kv); 
+                                       if (cis != null) {
+                                               try {
+                                                       ChildReference childPath = ChildReference.concatenate(le.path, new KeyReference(kv) );
+                                                       sa.addListener(le.listener, cis, childPath, le.executor );
+                                               } catch (AccessorException e1) {
+                                                       throw new AccessorConstructionException(e1);
+                                               }
+                                       }
+                                               
+                                       // Next listener
+                                       le = le.next;
+                               }
+                       }
+                       
+                       return (T) sa;
+               } catch (AdaptException e) {
+                       throw new AccessorConstructionException(e);
+               } catch (AdapterConstructionException e) {
+                       throw new AccessorConstructionException(e);
+               } catch (IOException e) {
+                       throw new AccessorConstructionException(e);
+               } catch (AccessorException e) {
+                       throw new AccessorConstructionException(e);
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+       
+       @Override
+       public Object[] getValues(Binding valueBinding) throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {\r
+                       Serializer rvs = params.serializerScheme.getSerializer(valueBinding);
+                       b.position(0L);
+                       int count = b.readInt();
+                       Object[] result = new Object[ count ];
+                       for (int i=0; i<result.length; i++) {
+                               ks.skip(b, null);
+                               result[i] = rvs.deserialize(b, null);
+                       }
+                       return result;
+               } catch (IOException e) {
+                       throw new AccessorException( e );
+               } catch (SerializerConstructionException e) {\r
+                       throw new AccessorException( e );\r
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+
+       @Override
+       public void setValueNoflush(Binding binding, Object mapValue)
+                       throws AccessorException {              
+               assert b.isOpen();\r
+               MapBinding mb = (MapBinding) binding;
+               if (children.isEmpty() && listeners==null) {\r
+                       writeLock();
+                       try {
+                               // Write
+                               Serializer s = params.serializerScheme.getSerializer( mb );
+                               int size = s.getSize(mapValue, null);
+                               b.setLength(size);
+                               b.position(0L);
+                               // Write all at once
+                               s.serialize(b, null, mapValue);                 
+                       } catch (IOException e) {
+                               throw new AccessorException( e );
+                       } catch (SerializerConstructionException e) {\r
+                               throw new AccessorException( e );\r
+                       } finally {\r
+                               writeUnlock();\r
+                       }
+               } else {
+\r
+                       writeLock();
+                       try {
+                               int nc = mb.size(mapValue);
+                               Object nks[] = new Object[nc];
+                               Object nvs[] = new Object[nc];
+                               mb.getAll(mapValue, nks, nvs);
+                               setAllNoflush(mb.getKeyBinding(), mb.getValueBinding(), nks, nvs);
+                       } catch (BindingException e) {
+                               throw new AccessorException(e);
+                       } finally {\r
+                               writeUnlock();\r
+                       }
+                       
+               }
+       }
+       
+
+       @Override
+       public void clearNoflush() throws AccessorException {
+               assert b.isOpen();\r
+               writeLock();
+               try {
+                       boolean hasListeners = listeners!=null;
+                       Object[] keys = hasListeners ? getKeys(kb) : null; 
+                       
+                       // Write
+                       b.position(0L);
+                       b.writeInt(0);
+                       b.setLength(4);
+                                               
+                       // Disconnect sub-accessor
+                       for (java.lang.ref.Reference<BinaryObject> ref : children.values()) {
+                               BinaryObject sa = ref.get();
+                               if (sa==null) continue;
+                               sa.invalidatedNotification();
+                       }
+                       children.clear();
+                       
+                       // Notify Listeners
+                       ListenerEntry le = listeners;
+                       while (le!=null) {                              
+                               MapInterestSet is = le.getInterestSet();
+                               for (Object key : keys) {
+                                       MutableVariant var = new MutableVariant(kb, key);
+                                       if (is.inNotificationsOf(var)) {
+                                               MapEntryRemoved e = new MapEntryRemoved(var); 
+                                               emitEvent(le, e);
+                                       }
+                               }
+                                                               
+                               le = le.next;
+                       }               
+                       
+               } catch (IOException e) {
+                       throw new AccessorException( e );
+               } finally {\r
+                       writeUnlock();\r
+               }
+               
+       }
+
+       @Override
+       public void putAllNoflush(Binding keyBinding, Binding valueBinding,
+                       Map<Object, Object> from) throws AccessorException {
+               int nc = from.size();
+               Object[] nks = new Object[ nc ];
+               Object[] nvs = new Object[ nc ];                
+           int i=0;
+           for (Map.Entry<Object, Object> e : from.entrySet()) {
+               nks[i] = e.getKey();
+               nvs[i] = e.getValue();
+               i++;
+           }
+               putAllNoflush(keyBinding, valueBinding, nks, nvs);
+       }
+
+       @Override
+       public void putAllNoflush(Binding kb, Binding vb,
+                       Object[] nks, Object[] nvs) throws AccessorException {
+               int c = nks.length;
+               int newItemsCount = 0;
+               for (int i=0; i<c; i++) {
+                       boolean hadPreviousValue = _putNoflush(kb, nks[i], vb, nvs[i]);
+                       if (!hadPreviousValue) newItemsCount++;
+               }
+
+               if (newItemsCount>0) {\r
+                       assert b.isOpen();\r
+                       writeLock();
+                       try {
+                               b.position(0L);
+                               int oldCount = b.readInt();
+                               b.position(0L);\r
+                               b.writeInt( oldCount + newItemsCount);                  
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       } finally {\r
+                               writeUnlock();\r
+                       }
+               }
+       
+       }
+       
+       public void setAllNoflush(Binding kb, Binding vb,
+                       Object[] nks, Object[] nvs) throws AccessorException {\r
+               assert b.isOpen();\r
+               writeLock();
+               try {
+                       Serializer ks = params.serializerScheme.getSerializer( kb );
+                       Serializer vs = params.serializerScheme.getSerializer( vb );
+                       int nc = nks.length;
+                       // Generate removed, assigned and added events
+                       if (listeners!=null) {
+                               
+                               b.position(4L);                 
+                               int ni = 0;
+                               
+                               // New and Old map lowest Keys 
+                               Object nk = nks.length>0 ? nks[0] : null;
+                               Object ok = b.length()<=4 ? null : ks.deserialize(b, null);
+                               
+                               while (nk!=null || ok!=null) {
+                                       int c = 0;
+                                       if (nk==null) {
+                                               c = -1;
+                                       } else if (ok==null) {
+                                               c= 1;
+                                       } else c = kb.compare(ok, nk);
+                                       if (c==0) {
+                                               // old key == new key
+                                               // Assigned new value
+                                               MutableVariant kv = new MutableVariant(kb, kb.isImmutable() ? nk : kb.clone(nk));
+                                               ListenerEntry le = listeners;
+                                               while (le!=null) {                              
+                                                       MapInterestSet is = le.getInterestSet();
+                                                       if (is.inNotificationsOf(kv)) {
+                                                               MutableVariant vv = null;
+                                                               if (is.inValuesOf(kv)) {
+                                                                       Object nv = vb.isImmutable() ? nvs[ni] : vb.clone(nvs[ni]);
+                                                                       vv = new MutableVariant(vb, nv);                                                                        
+                                                               }
+                                                               Event e = new ValueAssigned( new KeyReference( kv ), vv);
+                                                               emitEvent(le, e);
+                                                       }
+                                                       le = le.next;
+                                               }
+                                               // Move new pointer
+                                               ni++;
+                                               nk = ni<nks.length ? nks[ni] : null;
+                                               // Move old pointer
+                                               vs.skip(b, null);
+                                               ok = b.position()<b.length() ? ks.deserialize(b, null) : null;                                                  
+                                       } 
+                                       
+                                       else if (c<0) {
+                                               // old key < new key
+                                               MutableVariant kv = new MutableVariant(kb, kb.isImmutable() ? ok : kb.clone(ok));
+                                               ListenerEntry le = listeners;
+                                               while (le!=null) {                              
+                                                       MapInterestSet is = le.getInterestSet();
+                                                       if (is.inNotificationsOf(kv)) {                                                         
+                                                               MapEntryRemoved e = new MapEntryRemoved(kv);
+                                                               emitEvent(le, e);
+                                                       }
+                                                       le = le.next;
+                                               }                                               
+                                               // Move old pointer
+                                               vs.skip(b, null);
+                                               ok = b.position()<b.length() ? ks.deserialize(b, null) : null;                                                  
+                                       }
+                                       
+                                       else if (c>0) {
+                                               // new key < old key
+                                               MutableVariant kv = new MutableVariant(kb, kb.isImmutable() ? nk : kb.clone(nk));
+
+                                               if (listeners!=null) {
+                                                       Object nv = null;
+                                                       ListenerEntry le = listeners;
+                                                       while (le!=null) {                              
+                                                               MapInterestSet is = le.getInterestSet();
+                                                               if (is.inNotificationsOf(kv)) {                                                         
+                                                                       MutableVariant vv = null;
+                                                                       if (is.inValuesOf(kv)) {
+                                                                               if (nv==null) {
+                                                                                       nv = vb.clone( nvs[ni] );
+                                                                               }
+                                                                               vv = new MutableVariant(vb, nv);
+                                                                       }
+                                                                       MapEntryAdded e = new MapEntryAdded(kv, vv);
+                                                                       emitEvent(le, e);
+                                                               }
+                                                               le = le.next;
+                                                       }
+                                               }
+                                               
+                                               // Move new pointer
+                                               ni++;
+                                               nk = ni<nks.length ? nks[ni] : null;
+                                       }
+                               }
+                               
+                       }
+                       
+                       // Calc Size
+                       long size = 4;                  
+                       if (ks.getConstantSize()!=null) {
+                               size += ((long)nc) * ks.getConstantSize();
+                       } else {
+                               for (int i=0; i<nc; i++) size += ks.getSize(nks[i], null);
+                               
+                       }
+                       if (vs.getConstantSize()!=null) {
+                               size += ((long)nc) * vs.getConstantSize();
+                       } else {
+                               for (int i=0; i<nc; i++) size += vs.getSize(nvs[i], null);
+                               
+                       }
+                       b.setLength(size);                      
+                       
+                       // Write entry by entry and update positions of children
+                       b.position(0L);
+                       b.writeInt(nc);
+                       for (int i=0; i<nc; i++) {
+                               Object nk = nks[i];
+                               Object nv = nvs[i];
+                               long startPos = b.position();
+                               ks.serialize(b, null, nk);
+                               vs.serialize(b, null, nv);
+                               long endPos = b.position();
+                               long len = endPos - startPos;\r
+                               Object lk = params.adapterScheme.adapt(nk, kb, this.kb);
+                               BinaryObject sa = getChild(lk);
+                               if (sa!=null) sa.b.setPositionInSource(startPos, len);                                  
+                       }
+                               
+                       
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               } catch (SerializerConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } finally {\r
+                       writeUnlock();\r
+               }
+       }
+
+       
+       @Override
+       public void putNoflush(Binding kb, Object key,
+                       Binding vb, Object value) throws AccessorException {\r
+               assert b.isOpen();\r
+               writeLock();
+               try {
+                       boolean hadPreviousEntry = _putNoflush(kb, key, vb, value);
+               
+                       if (!hadPreviousEntry) {
+                               b.position(0L);
+                               int oldCount = b.readInt();
+                               b.position(0L);
+                               b.writeInt( oldCount + 1);                      
+                       }
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } catch (RuntimeSerializerConstructionException e) {
+                       throw new AccessorException(e);
+               } finally {\r
+                       writeUnlock();\r
+               }
+       }
+       
+       /**
+        * Put an entry. Does not update size.
+        * 
+        * @param kb
+        * @param key
+        * @param vb
+        * @param value
+        * @return true if previous entry existed
+        * @throws AccessorException
+        */
+       private boolean _putNoflush(Binding kb, Object key,
+                       Binding vb, Object value) throws AccessorException {
+               writeLock();
+               try {
+                       Object lk = adapt(key, kb, this.kb);\r
+                       long insertPos = index.getInsertPos(lk);
+                       Serializer ks = params.serializerScheme.getSerializer( kb );
+                       Serializer vs = params.serializerScheme.getSerializer( vb );                    
+                       long newSize = ks.getSize(key, null) + vs.getSize(value, null);
+                       
+                       if (insertPos<0) {                              
+                               // Insert new entry
+                               // Write
+                               long pos = -insertPos;
+                               b.position(pos);
+                               b.insertBytes(newSize, ByteSide.Neither);
+                               b.position(pos);
+                               ks.serialize(b, null, key);
+                               vs.serialize(b, null, value);
+                               
+                               // Key variant
+                               MutableVariant kv = new MutableVariant(this.kb, lk);
+                               
+                               // Notify Listeners
+                               ListenerEntry le = listeners;
+                               while (le!=null) {                              
+                                       MapInterestSet is = le.getInterestSet();
+                                       if (is.inNotificationsOf(kv)) {
+                                               
+                                               MutableVariant vv = null;
+                                               if (is.inValuesOf(kv)) vv = new MutableVariant(vb, vb.isImmutable() ? value : vb.clone(value));
+                                               
+                                               // Notify about new entry
+                                               MapEntryAdded e = new MapEntryAdded(kv, vv);
+                                               emitEvent(le, e);
+                                       }
+                                       
+                                       le = le.next;
+                               }
+                               
+                               // Optimization for sequential write
+                               // Create sub accessor for the last entry
+                               if (insertPos==b.length() && constantSize==null) {
+                                       BinaryObject sa = getChild(lk);
+                                       if (sa==null) {
+                                               // Instantiate correct sub accessor. 
+                                               try {
+                                                       sa = createSubAccessor(vb.type(), pos, newSize, params);
+                                                       children.put(lk, new SoftReference<BinaryObject>(sa));
+                                               } catch (AccessorConstructionException e1) {
+                                               }
+                                       }
+                               }
+                               
+                               return false;
+                       }
+                       
+                       // Replace previous entry                       
+                       // Write
+                       Entry e = new Entry(lk, insertPos, null);
+                       long pos = insertPos;
+                       long oldSize = index.size(e);
+                       
+                       if (oldSize<newSize) {
+                               b.position(pos);
+                               b.insertBytes(newSize - oldSize, ByteSide.Right);
+                       } else if (newSize<oldSize) {
+                               b.position(pos);
+                               b.removeBytes(oldSize - newSize, ByteSide.Right);
+                       }
+                       
+                       b.position(pos);
+                       ks.serialize(b, null, key);
+                       vs.serialize(b, null, value);
+                                               
+                       // Notify Listeners
+                       ListenerEntry le = listeners;
+                       if (listeners!=null) {
+                               MutableVariant kv = new MutableVariant(kb, lk);
+                               while (le!=null) {                              
+                                       MapInterestSet is = le.getInterestSet();
+                                       if (is.inNotificationsOf(kv)) {
+                                               
+                                               MutableVariant vv = null;
+                                               if (is.inValuesOf(kv)) vv = new MutableVariant(vb, vb.isImmutable() ? value : vb.clone(value));
+                                               
+                                               // Notify about new entry
+                                               Event ev = new ValueAssigned( new KeyReference(kv), vv);
+                                               emitEvent(le, ev);
+                                       }
+                                       
+                                       le = le.next;
+                               }
+                       }
+
+                       // Update child
+//                     BinaryObject sa = getChild(lk);                         
+//                     if (sa!=null) {
+//                             sa.b.setPositionInSource(pos, newSize);
+//                     }
+                       
+                       return true;
+                       
+               } catch (AdaptException e1) {
+                       throw new AccessorException(e1);
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } catch (RuntimeSerializerConstructionException e) {
+                       throw new AccessorException(e);
+               } catch (AdapterConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } catch (SerializerConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } finally {\r
+                       writeUnlock();\r
+               }
+               
+       }
+
+       @Override
+       public void removeNoflush(Binding keyBinding, Object key)
+                       throws AccessorException {\r
+               assert b.isOpen();\r
+               writeLock();
+               try {
+                       Object lk = params.adapterScheme.getAdapter(keyBinding, kb, true, listeners!=null).adapt(key);
+                       Entry e = index.getKey(lk);
+                       if (e==null) return;
+                       // Write
+                       int oldSize = size();
+                       b.position(0L);
+                       b.writeInt( oldSize - 1 );
+                       long size = index.size(e);
+                       b.position(e.pos);
+                       b.removeBytes(size, ByteSide.Right);
+                       
+                       // Disconnect sub-accessor
+                       BinaryObject sa = getChild(lk);
+                       // Notify about disconnection of sub-accessor
+                       if (sa!=null) {
+                               sa.invalidatedNotification();
+                               children.remove(lk);
+                       }                               
+                       
+                       // Notify Listeners
+                       if (listeners!=null) {
+                               MutableVariant var = new MutableVariant(kb, lk);
+                               ListenerEntry le = listeners;
+                               while (le!=null) {                              
+                                       MapInterestSet is = le.getInterestSet();                                
+                                       if (is.inNotificationsOf(var)) {
+                                               MapEntryRemoved e2 = new MapEntryRemoved(var); 
+                                               emitEvent(le, e2);
+                                       }
+                                                                       
+                                       le = le.next;
+                               }               
+                       }
+                       
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               } catch (AdapterConstructionException e) {
+                       throw new AccessorException(e);
+               } finally {\r
+                       writeUnlock();\r
+               }
+       }
+       
+       
+       @Override
+       public void addListener(Listener listener, InterestSet interestSet,
+                       ChildReference path, Executor executor) throws AccessorException {
+               super.addListener(listener, interestSet, path, executor);
+               MapInterestSet is = (MapInterestSet) interestSet;
+
+               for (Object key : children.keySet()) {
+                       BinaryObject sa = getChild(key);
+                       if (sa==null) continue;
+                       
+                       MutableVariant vkey = new MutableVariant(kb, key);
+                       InterestSet cis = is.getComponentInterest();
+                       if (cis!=null) {
+                               ChildReference childPath = ChildReference.concatenate( path, new KeyReference(vkey) );
+                               sa.addListener(listener, cis, childPath, executor);                             
+                       }
+                       cis = is.getComponentInterest( vkey );
+                       if (cis!=null) {
+                               ChildReference childPath = ChildReference.concatenate( path, new KeyReference(vkey) );
+                               sa.addListener(listener, cis, childPath, executor);                             
+                       }
+               }               
+       }               
+       
+       @Override
+       public void removeListener(Listener listener) throws AccessorException {
+               ListenerEntry e = detachListener(listener);
+               if (e==null) return;
+               MapInterestSet is = (MapInterestSet) e.interestSet;
+               
+               for (Map.Entry<Object, java.lang.ref.Reference<BinaryObject>> entry : children.entrySet()) {
+                       BinaryObject sa = entry.getValue().get();
+                       if (sa==null) continue;
+                       Object key = entry.getKey();
+                       
+                       MutableVariant vkey = new MutableVariant(kb, key);
+                       InterestSet cis = is.getComponentInterest();
+                       if (cis!=null) {
+                               sa.removeListener(listener);                            
+                       }
+                       cis = is.getComponentInterest( vkey );
+                       if (cis!=null) {
+                               sa.removeListener(listener);                            
+                       }
+               }
+               
+       }
+       
+       @Override
+       Event applyLocal(Event e, boolean makeRollback) throws AccessorException {
+               Event rollback = null;
+               
+               if (e instanceof ValueAssigned) {\r
+                       try {\r
+                               ValueAssigned va = (ValueAssigned) e;\r
+                               if (makeRollback) {\r
+                                       Binding binding = params.bindingScheme.getBinding(type());\r
+                                       rollback = new ValueAssigned(binding, getValue(binding));                               \r
+                               }\r
+                               setValueNoflush(va.newValue.getBinding(), va.newValue.getValue());\r
+                               return rollback;\r
+                       } catch (BindingConstructionException e1) {\r
+                               throw new AccessorException( e1 );\r
+                       }\r
+               } else if (e instanceof MapEntryAdded) {
+                       MapEntryAdded ea = (MapEntryAdded) e;
+                       if (ea.key==null) throw new AccessorException("Cannot apply entry added event because key is missing");
+                       if (ea.value==null) throw new AccessorException("Cannot apply entry added event because value is missing");
+                       boolean hadValue = containsKey(ea.key.getBinding(), ea.key.getValue());
+                       if (hadValue) throw new AccessorException("Could not add entry to key that already existed");
+                       
+                       if (makeRollback) {                             
+                               rollback = new MapEntryRemoved( ea.key );
+                       }
+                       
+                       putNoflush(ea.key.getBinding(), ea.key.getValue(), ea.value.getBinding(), ea.value.getValue());
+                       return rollback;\r
+                       
+               } else if (e instanceof MapEntryRemoved) {
+                       MapEntryRemoved er = (MapEntryRemoved) e;
+                       
+                       if (makeRollback) {
+                               boolean hadValue = containsKey(er.key.getBinding(), er.key.getValue());
+                               
+                               if (hadValue) {                         
+                                       Object oldValueObj = get(er.key.getBinding(), er.key.getValue(), vb);
+                                       MutableVariant oldKey = er.key;
+                                       MutableVariant oldValue = new MutableVariant(vb, oldValueObj);
+                                       rollback = new MapEntryAdded(oldKey, oldValue);
+                               } else {
+                                       rollback = new MapEntryRemoved( er.key.clone() );
+                               }
+                       }
+                       
+                       removeNoflush( er.key.getBinding(), er.key.getValue() );
+                       return rollback;\r
+                       
+               } else {\r
+                       throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Map Type");\r
+               }               
+       }
+
+       
+       
+       @Override
+       public void put(Binding keyBinding, Object key, Binding valueBinding,
+                       Object value) throws AccessorException {\r
+               assert b.isOpen();\r
+               writeLock();
+               try {
+                       putNoflush(keyBinding, key, valueBinding, value);
+                       b.flush();
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } finally {\r
+                       writeUnlock();\r
+               }
+       }
+
+       @Override
+       public void putAll(Binding keyBinding, Binding valueBinding,
+                       Map<Object, Object> from) throws AccessorException {\r
+               assert b.isOpen();\r
+               writeLock();
+               try {
+                       putAllNoflush(keyBinding, valueBinding, from);
+                       b.flush();
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } finally {\r
+                       writeUnlock();\r
+               }
+       }
+
+       @Override
+       public void putAll(Binding keyBinding, Binding valueBinding, Object[] keys,
+                       Object[] values) throws AccessorException {\r
+               assert b.isOpen();\r
+               writeLock();
+               try {
+                       putAllNoflush(keyBinding, valueBinding, keys, values);
+                       b.flush();
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } finally {\r
+                       writeUnlock();\r
+               }
+       }
+
+       @Override
+       public void remove(Binding keyBinding, Object key) throws AccessorException {\r
+               assert b.isOpen();\r
+               writeLock();
+               try {
+                       removeNoflush(keyBinding, key);         
+                       b.flush();
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } finally {\r
+                       writeUnlock();\r
+               }
+       }
+       
+       
+       /** Interface for a search structure */
+       abstract class Index {
+               /**
+                * Get insert pos
+                * @param key
+                * @return if >0 position of existing entry of same key, <0 insertion position 
+                * @throws AccessorException \r
+                */
+               abstract long getInsertPos(Object key) throws AccessorException;
+
+               abstract Entry getKey(Object key) throws AccessorException;             
+               abstract Entry floor(Object key) throws AccessorException;
+               abstract Entry ceiling(Object key) throws AccessorException;
+               abstract Entry higher(Object key) throws AccessorException;
+               abstract Entry lower(Object key) throws AccessorException;
+               abstract long size(Entry e) throws AccessorException;           
+               
+               Entry first() throws AccessorException {
+                       try {
+                               b.position(0L);
+                               int count = b.readInt();
+                               if (count==0) return null;
+                               Object k = ks.deserialize(b);
+                               java.lang.ref.Reference<BinaryObject> ref = children.get(k);
+                               BinaryObject sa = ref == null ? null : ref.get();
+                               return new Entry(k, 4, sa);             
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       }
+               }
+               
+               abstract Entry last() throws AccessorException;
+                               
+       }       
+       
+       static class Entry {
+               // Optional child accessor
+               BinaryObject accessor;
+               Object key;
+               long pos;
+               public Entry(Object key, long pos, BinaryObject accessor) {
+                       this.key = key; this.pos = pos; this.accessor = accessor;
+               }
+       }
+
+       /** Entries are indexed a table */
+//     class Table implements Index {          
+//     }
+       
+       /** Entry size is constant, binary Search */ 
+       class Constant extends Index {
+               
+               Entry last() throws AccessorException {
+                       try {
+                               if (b.length()<=4) return null;
+                               long pos = b.length() - constantSize;
+                               b.position( pos );
+                               Object key = ks.deserialize(b);
+                               return new Entry(key, pos, null);
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       }               
+               }
+               
+
+               @Override
+               long getInsertPos(Object key) throws AccessorException {
+                       try {
+                               int count = count();
+                               if (count==0) return -4L;                       
+                               
+                               // Check if it is the last entry
+                               long pos = b.length() - constantSize;
+                               b.position( pos );
+                               Object lastKey = ks.deserialize(b);
+                               int c = kb.compare(lastKey, key);
+                               if (c<0) return -b.length();
+                               if (c==0) return pos;
+                               
+                               // We can narrow down the search to region between two existing children 
+                               Entry fc = floorChildEntry(key);
+                               Entry cc = ceilingChildEntry(key);                      
+                               
+                               int fromIndex = fc == null ? 0 : indexOf(fc);
+                               int toIndex = cc == null ? count : indexOf(cc)+1; 
+                               
+                               // Do binary search                     
+                               int r = binarySearch(fromIndex, toIndex, key);
+                               if (r>=0) return getPos(r);
+                               r=-(r+1);
+                               if (r==count) return -b.length();
+                               return -getPos(r);
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       } catch (BindingException e) {
+                               throw new AccessorException(e);
+                       }               
+               }       
+               
+               int indexOf(Entry e) {
+                       return (int) ((e.pos - 4L) / constantSize);
+               }
+               int indexOf(long pos) {
+                       return (int) ((pos - 4L) / constantSize);
+               }
+               \r
+               \r
+               /**\r
+                * \r
+                * @param fromIndex\r
+                * @param toIndex exclusive\r
+                * @param key\r
+                * @return\r
+                * @throws AccessorException\r
+                */
+               int binarySearch(int fromIndex, int toIndex, Object key) throws AccessorException {
+                       int low = fromIndex;
+                       int high = toIndex - 1;
+       
+                       while (low <= high) {
+                           int mid = (low + high) >>> 1;
+                           Object midVal = getKey(mid);
+                           int cmp = kb.compare(midVal, key);
+       
+                           if (cmp < 0)
+                               low = mid + 1;
+                           else if (cmp > 0)
+                               high = mid - 1;
+                           else
+                               return mid; // key found
+                       }
+                       return -(low + 1);  // key not found.                   
+               }
+
+               @Override
+               public Entry getKey(Object key) throws AccessorException {
+                       BinaryObject sa = getChild(key);
+                       if (sa!=null) return new Entry(key, sa.b.getStartPositionInSourceBinary(), sa);
+                       long pos = getInsertPos(key);
+                       if (pos<0) return null;                 
+                       return new Entry(key, pos, null);
+               }
+
+               @Override
+               public long size(Entry e) {                     
+                       return constantSize;
+               }
+               
+               Object getKey(int index) throws AccessorException {
+                       try {
+                               long pos = 4L + ((long)index) * constantSize;
+                               b.position(pos);
+                               return ks.deserialize(b);
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       }               
+               }
+
+               Entry getEntry(int index) throws AccessorException {
+                       try {
+                               long pos = 4L + ((long)index) * constantSize;
+                               b.position(pos);
+                               Object key = ks.deserialize(b);
+                               return new Entry(key, pos, null);
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       }               
+               }
+
+               long getPos(int index) {
+                       return 4L + ((long)index) * constantSize;
+               }
+               
+               int count() throws IOException {
+                       return (int) ((b.length()-4) / constantSize);
+               }
+               
+               @Override
+               Entry ceiling(Object key) throws AccessorException {
+                       try {
+                               long pos = getInsertPos(key);
+                               // Exact match
+                               if (pos>0) return new Entry(key, pos, getChild(key));
+                               pos = -pos;
+                               int index = indexOf(pos);
+                               if (index>=count()) return null;
+                               key = getKeyAt(pos);
+                               return new Entry(key, pos, null);
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       } catch (BindingException e) {
+                               throw new AccessorException(e);
+                       }               
+               }
+
+               @Override
+               Entry higher(Object key) throws AccessorException {
+                       try {
+                               long pos = getInsertPos(key);
+       
+                               if (pos>0) {
+                                       // Exact match\r
+                                       int index = indexOf(pos)+1;
+                                       if (index>=count()) return null;\r
+                                       pos = getPos(index);
+                                       key = getKeyAt(pos);
+                                       return new Entry(key, pos, null);
+                               } else {\r
+                                       // Insert here match
+                                       int index = indexOf(-pos);
+                                       if (index>=count()) return null;\r
+                                       pos = getPos(index);
+                                       key = getKeyAt(pos);
+                                       return new Entry(key, pos, null);
+                               }                       
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       } catch (BindingException e) {
+                               throw new AccessorException(e);
+                       }               
+                       
+               }
+
+               @Override
+               Entry lower(Object key) throws AccessorException {
+                       try {
+                               long pos = getInsertPos(key);
+                               // Exact match
+                               if (pos>0) {
+                                       int index = indexOf(pos)-1;
+                                       if (index<0) return null;
+                                       pos = getPos(index);
+                                       key = getKeyAt(pos);
+                                       return new Entry(key, pos, null);
+                               } else {
+                                       int index = indexOf(-pos)-1;
+                                       if (index<0) return null;
+                                       pos = getPos(index);
+                                       key = getKeyAt(pos);
+                                       return new Entry(key, pos, null);
+                               }
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       } catch (BindingException e) {
+                               throw new AccessorException(e);
+                       }               
+               }
+
+               @Override
+               Entry floor(Object key) throws AccessorException {
+                       try {
+                               long pos = getInsertPos(key);
+                               // Exact match
+                               if (pos>0) return new Entry(key, pos, getChild(key));
+                               int index = indexOf(-pos)-1;
+                               if (index<0) return null;
+                               pos = getPos(index);            
+                               key = getKeyAt(pos);
+                               return new Entry(key, pos, null);
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       } catch (BindingException e) {
+                               throw new AccessorException(e);
+                       }               
+               }
+
+       }
+       
+       /** Variable size enetry, Sequential search */
+       class Sequential extends Index {
+\r
+               /**\r
+                * Get insert pos\r
+                * @param key\r
+                * @return if >0 position of existing entry of same key, <0 insertion position \r
+                * @throws AccessorException \r
+                */\r
+               @Override
+               long getInsertPos(Object key) throws AccessorException {
+                       try {
+                               Entry start = floorChildEntry(key);
+                               // Exact match
+                               if (start !=null && kb.equals(key, start.key)) {
+                                       return start.pos;
+                               }
+                               
+                               long len = b.length();
+                               long pos = start==null ? 4L : start.pos;
+                               b.position(pos);
+                               while (b.position()<len) {
+                                       pos = b.position();
+                                       Object k = ks.deserialize(b, null);
+                                       int c = kb.compare(key, k);
+                                       if (c==0) return pos;
+                                       if (c<0) return -pos;
+                                       vs.skip(b, null);
+                               }
+                               return -len;
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       } catch (BindingException e) {
+                               throw new AccessorException(e);
+                       }               
+               }
+               
+               @Override
+               Entry getKey(Object key) throws AccessorException {
+                       BinaryObject sa = getChild(key);
+                       if (sa!=null) return new Entry(key, sa.b.getStartPositionInSourceBinary(), sa);
+                       
+                       long pos = getInsertPos(key);
+                       if (pos<0) return null;
+                       return new Entry(key, pos, null);
+               }
+
+               @Override
+               Entry last() throws AccessorException {
+                       try {
+                               Entry e = lastChildEntry();     \r
+                               long startPos = e!=null ? e.pos : 4L;\r
+                               b.position(startPos);\r
+                               Entry result = e!=null ? new Entry(e.key, e.pos, e.accessor) : new Entry(null, 0, null);\r
+                               while (b.position() < b.length()) {
+                                       result.key = ks.deserialize(b, null);\r
+                                       result.pos = b.position();                                      
+                                       vs.skip(b, null);
+                                       long valueLen = b.position() - result.pos;
+                                       result.accessor = createSubAccessor(vb.type(), result.pos, valueLen, params);
+                                       children.put(result.key, new SoftReference<BinaryObject>( result.accessor ));
+                               }\r
+                               return (result.key == null) ? null : result;
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       } catch (BindingException e) {
+                               throw new AccessorException(e);
+                       } catch (AccessorConstructionException e) {
+                               throw new AccessorException(e);
+                       }
+               }
+
+               @Override
+               long size(Entry e) throws AccessorException {
+                       try {
+                               long keyLen;
+                               if (ks.getConstantSize()!=null) {
+                                       keyLen = ks.getConstantSize();
+                               } else {
+                                       b.position(e.pos);
+                                       ks.skip(b, null);
+                                       keyLen = b.position() - e.pos;
+                               }
+                               
+                               long valueLen;                          
+                               if (e.accessor!=null) {
+                                       valueLen = e.accessor.b.length();
+                               } else if (vs.getConstantSize()!=null) {
+                                       valueLen = vs.getConstantSize();
+                               } else {
+                                       long pos = e.pos + keyLen;
+                                       b.position(pos);
+                                       vs.skip(b, null);
+                                       valueLen = b.position() - pos; 
+                               }
+                               return keyLen + valueLen;
+                       } catch (IOException e1) {
+                               throw new AccessorException(e1);
+                       }
+               }
+
+               @Override
+               Entry ceiling(Object key) throws AccessorException {
+                       try {
+                               long pos = getInsertPos(key);
+                               // Exact match
+                               if (pos>0) return new Entry(key, pos, getChild(key));
+                               pos = -pos;
+                               return getEntryAt(pos);
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       } catch (BindingException e) {
+                               throw new AccessorException(e);
+                       }
+               }
+
+               @Override
+               Entry higher(Object key) throws AccessorException {
+                       try {
+                               long pos = getInsertPos(key);
+                               // Exact match
+                               if (pos>0) {
+                                       if (pos>=b.length()) return null;
+                                       b.position(pos);
+                                       ks.skip(b, null);
+                                       vs.skip(b, null);
+                                       return getEntryAt(b.position());
+                               }
+                               pos = -pos;
+                               return getEntryAt(pos);
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       } catch (BindingException e) {
+                               throw new AccessorException(e);
+                       }               
+               }
+
+               @Override
+               Entry lower(Object key) throws AccessorException {
+                       try {
+                               Entry start = lowerChildEntry(key);
+                               long len = b.length();
+                               long pos = start==null ? 4L : start.pos;
+                               long prevPos = 0;
+                               Object k = null;
+                               Object prevK = null;
+                               b.position(pos);
+                               while (b.position()<len) {
+                                       prevPos = pos;
+                                       pos = b.position();
+                                       prevK = k;
+                                       k = ks.deserialize(b, null);
+                                       long valuePos = b.position();\r
+                                       \r
+                                       // Compare current key with search key
+                                       int c = kb.compare(key, k);\r
+                                       if (c<=0) {
+                                               if (prevK==null) return null;
+                                               return new Entry(prevK, prevPos, null);
+                                       }
+                                       vs.skip(b, null);
+                                       long valueLen = b.position() - valuePos;\r
+                                       assert(valueLen>=0);
+                                       java.lang.ref.Reference<BinaryObject> ref = children.get(k);
+                                       BinaryObject sa = ref!=null?ref.get():null;
+                                       if (sa==null) {
+                                               sa = createSubAccessor(vb.type(), valuePos, valueLen, params);
+                                               children.put(k, new SoftReference<BinaryObject>(sa));
+                                       }                                       
+                               }\r
+                               // There was no match, return the last entry
+                               return new Entry(k, pos, null); 
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       } catch (BindingException e) {
+                               throw new AccessorException(e);
+                       } catch (AccessorConstructionException e) {
+                               throw new AccessorException(e);
+                       }                       
+               }
+
+               @Override
+               Entry floor(Object key) throws AccessorException {
+                       try {                   
+                               Entry start = floorChildEntry(key);
+                               // Exact match
+                               if (start !=null && kb.equals(key, start.key)) {
+                                       return start;
+                               }
+                                                               
+                               long len = b.length();
+                               long pos = start==null ? 4L : start.pos;
+                               long prevPos = 0;
+                               Object k = null;
+                               Object prevK = null;\r
+                               BinaryObject prevAccessor = null;\r
+                               b.position(pos);
+                               while (b.position()<len) {
+                                       prevPos = pos;
+                                       pos = b.position();
+                                       prevK = k;
+                                       k = ks.deserialize(b, null);
+                                       long valuePos = b.position();\r
+                                       
+                                       int c = kb.compare(key, k);\r
+                                       // Went over
+                                       if (c<0) {
+                                               if (prevK==null) return null;
+                                               return new Entry(prevK, prevPos, prevAccessor);
+                                       }
+                                       vs.skip(b, null);
+                                       long valueLen = valuePos - pos;
+                                       java.lang.ref.Reference<BinaryObject> ref = children.get(k);
+                                       BinaryObject sa = ref!=null?ref.get():null;
+                                       if (sa==null) {
+                                               prevAccessor = createSubAccessor(vb.type(), valuePos, valueLen, params);
+                                               children.put(k, new SoftReference<BinaryObject>(prevAccessor));
+                                       }\r
+                                       // Exact match\r
+                                       if (c==0) {\r
+                                               return new Entry(k, pos, prevAccessor);\r
+                                       }                                       
+                               }
+                               return new Entry(k, pos, prevAccessor); 
+                       } catch (IOException e) {
+                               throw new AccessorException(e);
+                       } catch (BindingException e) {
+                               throw new AccessorException(e);
+                       } catch (AccessorConstructionException e) {
+                               throw new AccessorException(e);
+                       }
+               }
+       }
+       
+}
+