]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/impl/DirectoryMap.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / impl / DirectoryMap.java
index 647aab8239aab6b6c72ec314551069362230c450..5a94b04ca4cd51d3e3ce2b4d8e72696f4477dde5 100644 (file)
-/*******************************************************************************\r
- * Industry THTH ry.\r
- * Copyright (c) 2010- Association for Decentralized Information Management in\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.impl;\r
-\r
-import java.io.File;\r
-import java.io.FileFilter;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.HashSet;\r
-import java.util.Iterator;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Map.Entry;\r
-import java.util.Set;\r
-import java.util.TreeSet;\r
-import java.util.concurrent.Executor;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.Datatypes;\r
-import org.simantics.databoard.accessor.Accessor;\r
-import org.simantics.databoard.accessor.CloseableAccessor;\r
-import org.simantics.databoard.accessor.MapAccessor;\r
-import org.simantics.databoard.accessor.VariantAccessor;\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.FileLibrary;\r
-import org.simantics.databoard.accessor.file.FileVariantAccessor;\r
-import org.simantics.databoard.accessor.impl.DirectoryWatch.DirectoryEvent;\r
-import org.simantics.databoard.accessor.impl.DirectoryWatch.DirectoryListener;\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.VariantBinding;\r
-import org.simantics.databoard.binding.error.BindingConstructionException;\r
-import org.simantics.databoard.binding.error.BindingException;\r
-import org.simantics.databoard.binding.error.RuntimeBindingException;\r
-import org.simantics.databoard.binding.mutable.MutableVariant;\r
-import org.simantics.databoard.type.Datatype;\r
-import org.simantics.databoard.type.MapType;\r
-\r
-/**\r
- * DirectoryMap is a file backed map implementation where keys are filenames\r
- * and values are corresponding files. \r
- * <p>\r
- * This class is an implmentation to Map(Variant, Variant) -Accessor.\r
- * \r
- *  Filenames have the following encoding:\r
- *    S<string>.dbb   String types, if string doesn't have the following \r
- *                    control characters " : < > | ? * \ /   [0..31]\r
- *    I<integer>.dbb  Integer types\r
- *    L<long>.dbb     Long types\r
- *    H<hex>.dbb      All other cases the value as binary \r
- * <p>\r
- * File accessor is created if an entry opened as a sub-accessor.\r
- * The file accessor is closed when all sub-accessors are released.\r
- * The implementation is based on proxy instances and a reference queue.\r
- * Once the queue is empty, file accessor is closed.\r
- * <p>\r
- * DirectoryMap must be closed with #close();     \r
- *\r
- * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
- */\r
-public class DirectoryMap implements MapAccessor, CloseableAccessor {\r
-\r
-       /** Key binding */\r
-       final static Binding KEY_BINDING = Bindings.STR_VARIANT;\r
-       \r
-       /** Cache of sub-accessors */\r
-       FileLibrary files;\r
-       \r
-       /** Monitors directory for file changes */\r
-       DirectoryWatch dir;\r
-       \r
-       /** Folder */\r
-       File path;\r
-       \r
-       /** Listeners */\r
-       ListenerEntry listeners = null;\r
-       \r
-       /** Parent, optional */\r
-       Accessor parent; \r
-\r
-       /** Accessor params */\r
-       AccessorParams params;\r
-       \r
-       DirectoryListener dirListener = new DirectoryListener() {\r
-               @Override\r
-               public void onWatchEvent(DirectoryEvent e) {\r
-                       \r
-               }\r
-       };\r
-\r
-       public DirectoryMap(File directory) {\r
-               this(directory, null, AccessorParams.DEFAULT);\r
-       }\r
-\r
-       public DirectoryMap(File directory, Accessor parent) {\r
-               this(directory, parent, AccessorParams.DEFAULT);\r
-       }\r
-\r
-       public DirectoryMap(File directory, Accessor parent, AccessorParams params) {\r
-               this.parent = parent;\r
-               this.path = directory;\r
-               this.params = params;\r
-\r
-               // Filters .dbb files\r
-               FileFilter filter = new FileFilter() {\r
-                       public boolean accept(File pathname) {                          \r
-                               String filename = pathname.getName();\r
-                               if (filename.length()==0) return false;\r
-                               char c = filename.charAt(0);\r
-                               if (c!='S' && c!='I' && c!='L' && c!='B') return false;\r
-                               if (filename.endsWith(".dbb")) return true;\r
-                               return filename.toLowerCase().endsWith(".dbb");\r
-                       }};\r
-               \r
-               dir = new DirectoryWatch(path, filter);\r
-               \r
-               dir.addListener( dirListener );\r
-               \r
-               files = new FileLibrary();\r
-       }\r
-       \r
-       public void close() {\r
-               dir.removeListener( dirListener );\r
-               dir.close();\r
-               files.close();\r
-       }\r
-       \r
-       static MapType type = new MapType(Datatypes.VARIANT, Datatypes.VARIANT);\r
-       public MapType type() {\r
-               return type;\r
-       }\r
-\r
-       private String fileToKey(File f) {\r
-               String filename = f.getName();\r
-               String keyStr = filename.substring(0, filename.length()-4);\r
-               return keyStr;\r
-       }\r
-       \r
-       private File keyToFile(String keyStr) {\r
-               return new File(path, keyStr + ".dbb" );\r
-       }\r
-       \r
-       @Override\r
-       public void clear() throws AccessorException {\r
-               //List<File> deleteList = dir.files();\r
-               List<File> failList = new ArrayList<File>();\r
-               boolean hasListeners = listeners!=null;\r
-               List<String> keys = hasListeners ? new ArrayList<String>() : null;\r
-               \r
-               // Close all file handles\r
-               files.close();\r
-               \r
-               // Delete files\r
-               for (File f : dir.files()) {\r
-                       if (!files.deleteFile(f)) {\r
-                               failList.add(f);\r
-                       }\r
-                       if (hasListeners) {\r
-                               String keyStr = fileToKey(f);\r
-                               keys.add(keyStr);\r
-                       }\r
-               }\r
-\r
-               // Re-read directory\r
-               dir.refresh();\r
-               \r
-               // Notify Listeners\r
-               ListenerEntry le = listeners;\r
-               while (le!=null) {                              \r
-                       MapInterestSet is = le.getInterestSet();\r
-                       for (Object key : keys) {\r
-                               MutableVariant var = new MutableVariant(KEY_BINDING, key);\r
-                               if (is.inNotificationsOf(var)) {\r
-                                       MapEntryRemoved e = new MapEntryRemoved(var); \r
-                                       emitEvent(le, e);\r
-                               }\r
-                       }                                                       \r
-                       le = le.next;\r
-               }                               \r
-               \r
-               // Some files failed to delete\r
-               if (!failList.isEmpty()) {\r
-                       StringBuilder sb = new StringBuilder();\r
-                       sb.append("Failed to delete");\r
-                       for (File f : failList)  {\r
-                               sb.append(' ');\r
-                               sb.append(f.toString());\r
-                       }\r
-                       // HAX\r
-                       throw new AccessorException(sb.toString());\r
-               }\r
-               \r
-                               \r
-       }\r
-               \r
-       @Override\r
-       public String toString() {\r
-               return dir.toString();\r
-       }\r
-       \r
-       public Object getValue(Binding binding) throws AccessorException {\r
-               MapBinding mb = (MapBinding) binding; \r
-               if (mb.getKeyBinding() instanceof VariantBinding==false || mb.getValueBinding() instanceof VariantBinding==false)\r
-                       throw new AccessorException("Map(Variant, Variant) Expected");\r
-               // Get all files as a single map\r
-               try {\r
-                       Object result = binding.createDefault();\r
-                       for (File f : dir.files()) {\r
-                               // Create Key\r
-                               String keyStr = fileToKey(f);\r
-                               Object key = params.adapterScheme.adapt(keyStr, KEY_BINDING, mb.getKeyBinding());\r
-                               \r
-                               // Read value\r
-                               VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);\r
-                               Object value = va.getValue(mb.getValueBinding());\r
-                               \r
-                               mb.put(result, key, value);\r
-                       }\r
-                       return result;\r
-               } catch (BindingException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-       }\r
-       \r
-       @Override\r
-       public void getValue(Binding dstBinding, Object dst) throws AccessorException {\r
-               MapBinding db = (MapBinding) dstBinding; \r
-               Binding dkb = db.getKeyBinding();\r
-               Binding dvb = db.getValueBinding();\r
-               if (dkb instanceof VariantBinding==false || dvb instanceof VariantBinding==false)\r
-                       throw new AccessorException("Map(Variant, Variant) Expected");\r
-               // Get all files as a single map\r
-               try {\r
-                       TreeSet<Object> dstKeys = new TreeSet<Object>(dkb);\r
-                       db.getKeys(dst, dstKeys);\r
-                       \r
-                       for (File f : dir.files()) {\r
-                               // Create Key\r
-                               String keyStr = fileToKey(f);\r
-                               Object key = params.adapterScheme.adapt(keyStr, KEY_BINDING, dkb);\r
-                               \r
-                               Object v = db.containsKey(dst, key) ? db.get(dst, key) : dvb.createDefault();\r
-                               VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);\r
-                               va.getValue(dvb, v);\r
-                               \r
-                               db.put(dst, key, v);\r
-                               dstKeys.remove(key);\r
-                       }\r
-                       \r
-                       for (Object key : dstKeys)\r
-                               db.remove(dst, key);\r
-               } catch (BindingException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-               \r
-       }\r
-       \r
-       @Override\r
-       public boolean getValue(ChildReference path, Binding binding, Object obj) throws AccessorException {\r
-               try {\r
-                       Accessor a = getComponent(path);\r
-                       a.getValue(binding, obj);\r
-                       return true;\r
-               } catch (ReferenceException re) {\r
-                       return false;\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-       }       \r
-       \r
-       public Object getValue(ChildReference path, Binding binding) throws AccessorException {\r
-               try {\r
-                       Accessor a = getComponent(path);\r
-                       return a.getValue(binding);\r
-               } catch (ReferenceException re) {\r
-                       return null;\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-       }\r
-       \r
-       \r
-       @Override\r
-       public boolean containsKey(Binding keyBinding, Object key)\r
-                       throws AccessorException {\r
-               try {\r
-                       String key_ = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);\r
-                       File file = keyToFile(key_);\r
-                       return dir.files().contains(file);\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public boolean containsValue(Binding valueBinding, Object value)\r
-                       throws AccessorException {\r
-               try {\r
-                       for (File f : dir.files()) {\r
-                               String key = fileToKey(f);\r
-                               VariantAccessor va = getValueAccessor(KEY_BINDING, key);\r
-                               Object v = va.getValue(valueBinding);\r
-                               boolean match = valueBinding.equals(v, value);\r
-                               if ( match ) return true;\r
-                       }\r
-                       return false;\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               }               \r
-       }\r
-\r
-       @Override\r
-       public Object get(Binding keyBinding, Object key, Binding valueBinding)\r
-                       throws AccessorException {\r
-               try {\r
-                       VariantAccessor va = getValueAccessor(keyBinding, key);\r
-                       return va.getValue(valueBinding);\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Get the value as a variant\r
-        *  \r
-        * @param keyBinding\r
-        * @param key\r
-        * @return value\r
-        * @throws AccessorException\r
-        */\r
-       public MutableVariant getAsVariant(Binding keyBinding, Object key)\r
-                       throws AccessorException {\r
-               try {\r
-                       VariantAccessor va = getValueAccessor(keyBinding, key);\r
-                       Datatype type = va.getContentType();\r
-                       Binding binding = params.bindingScheme.getBinding(type);\r
-                       Object value = va.getContentValue(binding);\r
-                       MutableVariant result = new MutableVariant(binding, value);\r
-                       return result;\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (BindingConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public void getAll(Binding keyBinding, Binding valueBinding,\r
-                       Map<Object, Object> to) throws AccessorException {\r
-               try {\r
-                       for (File f : dir.files()) {    \r
-                               // Create key\r
-                               String keyStr = fileToKey(f);\r
-                               Object key = params.adapterScheme.adapt(keyStr, KEY_BINDING, keyBinding);\r
-\r
-                               // Read value\r
-                               VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);\r
-                               Object value = va.getValue(valueBinding);\r
-                               \r
-                               to.put(key, value);                             \r
-                       }\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               }                       \r
-       }\r
-\r
-       @Override\r
-       public void getAll(Binding keyBinding, Binding valueBinding, Object[] keys,\r
-                       Object[] values) throws AccessorException {\r
-               try {\r
-                       Set<String> fileKeys = createKeys();\r
-                       \r
-                       int i=0;\r
-                       for (String keyStr : fileKeys) {                                \r
-                               // Read value\r
-                               VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);\r
-                               Object value = va.getValue(valueBinding);\r
-                               \r
-                               Object key2 = params.adapterScheme.adapt(keyStr, KEY_BINDING, keyBinding);\r
-                               keys[i] = key2;\r
-                               values[i] = value;\r
-                               i++;\r
-                       }\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               }                       \r
-       }\r
-       \r
-\r
-       @Override\r
-       public int count(Binding keyBinding, Object from,\r
-                       boolean fromInclusive, Object end, boolean endInclusive)\r
-                       throws AccessorException {\r
-               throw new AccessorException("Not implemented");\r
-       }\r
-\r
-       @Override\r
-       public int getEntries(Binding keyBinding, Object from,\r
-                       boolean fromInclusive, Object end, boolean endInclusive,\r
-                       ArrayBinding keyArrayBinding, Object dstKeys,\r
-                       ArrayBinding valueArrayBinding, Object dstValues, int limit)\r
-                       throws AccessorException {\r
-               throw new AccessorException("Not implemented");\r
-       }\r
-       \r
-\r
-       TreeSet<String> createKeys() throws RuntimeBindingException {\r
-               List<File> files = dir.files();\r
-               TreeSet<String> keys = new TreeSet<String>(KEY_BINDING);\r
-               \r
-               for (File f : files) {\r
-                       String filename = f.getName();\r
-                       String str = filename.substring(0, filename.length()-4);\r
-                       keys.add(str);\r
-               }\r
-               \r
-               return keys;\r
-       }\r
-       \r
-       @Override\r
-       public Object getCeilingKey(Binding keyBinding, Object key)\r
-                       throws AccessorException {\r
-               try {\r
-                       TreeSet<String> keys = createKeys();\r
-                       String k = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);\r
-                       if (keys.contains(k)) return key;\r
-                       Object res = keys.ceiling(k);\r
-                       if (res==null) return null;\r
-                       return params.adapterScheme.adapt(res, KEY_BINDING, keyBinding);\r
-               } catch (RuntimeBindingException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public Object getFirstKey(Binding keyBinding) throws AccessorException {\r
-               List<File> files = dir.files();\r
-               String firstKey = null;\r
-               \r
-               for (File f : files) {\r
-                       String filename = f.getName();\r
-                       String str = filename.substring(0, filename.length()-4);\r
-                       if (firstKey == null) {\r
-                               firstKey = str;\r
-                       } else {\r
-                               if (KEY_BINDING.compare(str, firstKey)<0) firstKey = str;\r
-                       }\r
-               }\r
-               if (firstKey==null) return null;\r
-               \r
-               try {\r
-                       return params.adapterScheme.adapt(firstKey, KEY_BINDING, keyBinding);\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public Object getFloorKey(Binding keyBinding, Object key)\r
-                       throws AccessorException {\r
-               try {\r
-                       TreeSet<String> keys = createKeys();\r
-                       String k = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);\r
-                       Object res = keys.floor(k);\r
-                       if (res==null) return null;\r
-                       return params.adapterScheme.adapt(res, KEY_BINDING, keyBinding);\r
-               } catch (RuntimeBindingException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public Object getHigherKey(Binding keyBinding, Object key)\r
-                       throws AccessorException {\r
-               try {\r
-                       TreeSet<String> keys = createKeys();\r
-                       String k = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);\r
-                       Object res = keys.higher(k);\r
-                       if (res==null) return null;\r
-                       return params.adapterScheme.adapt(res, KEY_BINDING, keyBinding);\r
-               } catch (RuntimeBindingException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public Object[] getKeys(Binding keyBinding) throws AccessorException {\r
-               TreeSet<String> keys = createKeys();\r
-               Object[] result = new Object[keys.size()];\r
-               if (keys.isEmpty()) return result;\r
-               try {\r
-                       Adapter a = params.adapterScheme.getAdapter(KEY_BINDING, keyBinding, true, false);\r
-                       int index = 0;\r
-                       for (String key : keys) {                                                               \r
-                               result[index++] = a.adapt( key );\r
-                       }\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AdapterConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-               \r
-               return result;\r
-       }\r
-\r
-       @Override\r
-       public Object getLastKey(Binding keyBinding) throws AccessorException {\r
-               List<File> files = dir.files();\r
-               String lastKey = null;\r
-               \r
-               for (File f : files) {\r
-                       String filename = f.getName();\r
-                       String str = filename.substring(0, filename.length()-4);\r
-                       if (lastKey == null) {\r
-                               lastKey = str;\r
-                       } else {\r
-                               if (KEY_BINDING.compare(str, lastKey)>0) lastKey = str;\r
-                       }\r
-               }\r
-               if (lastKey==null) return null;\r
-               \r
-               try {\r
-                       return params.adapterScheme.adapt(lastKey, KEY_BINDING, keyBinding);\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public Object getLowerKey(Binding keyBinding, Object key)\r
-                       throws AccessorException {\r
-               try {\r
-                       TreeSet<String> keys = createKeys();\r
-                       String k = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);\r
-                       Object res = keys.lower(k);\r
-                       if (res==null) return null;\r
-                       return params.adapterScheme.adapt(res, KEY_BINDING, keyBinding);\r
-               } catch (RuntimeBindingException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-       }\r
-\r
-       public FileVariantAccessor getExistingAccessor(Binding keyBinding,\r
-                       Object key) throws AccessorConstructionException {\r
-               try {\r
-                       String key_ = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);\r
-                       File file = new File(path, key_ + ".dbb" );\r
-                       return files.getExistingFile(file);\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorConstructionException(e);\r
-               }       \r
-       }\r
-       \r
-       @SuppressWarnings("unchecked")\r
-       @Override\r
-       public FileVariantAccessor getValueAccessor(Binding keyBinding, Object key) throws AccessorConstructionException {\r
-               try {\r
-                       String keyStr = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);\r
-                       File file = keyToFile(keyStr);\r
-                       FileVariantAccessor sa = files.getExistingFile(file);\r
-                       if (sa!=null) return sa;\r
-                       \r
-                       // Create new accessor\r
-                       sa = files.getFile(file);\r
-\r
-                       // Add component interest sets\r
-                       ListenerEntry le = listeners;\r
-                       if (le!=null) {\r
-                               MutableVariant kv = new MutableVariant(keyBinding, key);\r
-                               while (le!=null) {                              \r
-                                       MapInterestSet is = le.getInterestSet();\r
-       \r
-                                       // Generic element interest\r
-                                       InterestSet gis = is.getComponentInterest(); \r
-                                       if (gis != null) {\r
-                                               try {\r
-                                                       ChildReference childPath = ChildReference.concatenate(le.path, new KeyReference(kv) );\r
-                                                       sa.addListener(le.listener, gis, childPath, le.executor);\r
-                                               } catch (AccessorException e) {\r
-                                                       throw new AccessorConstructionException(e);\r
-                                               }\r
-                                       }\r
-                                               \r
-                                       // Specific element interest\r
-                                       InterestSet cis = is.getComponentInterest(kv); \r
-                                       if (cis != null) {\r
-                                               try {\r
-                                                       ChildReference childPath = ChildReference.concatenate(le.path, new KeyReference(kv) );\r
-                                                       sa.addListener(le.listener, cis, childPath, le.executor);\r
-                                               } catch (AccessorException e) {\r
-                                                       throw new AccessorConstructionException(e);\r
-                                               }\r
-                                       }\r
-                                       \r
-                                       // Next listener\r
-                                       le = le.next;\r
-                               }\r
-                       }\r
-                       \r
-                       return sa;\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorConstructionException(e);\r
-               }       \r
-       }\r
-\r
-       @Override\r
-       public Object[] getValues(Binding valueBinding) throws AccessorException {              \r
-               try {\r
-                       Set<String> keys = createKeys();\r
-                       int count = keys.size();\r
-                       Object[] result = new Object[ count ];\r
-                       \r
-                       Iterator<String> iter = keys.iterator();\r
-                       for (int i=0; i<count; i++) {\r
-                               String keyStr = iter.next();\r
-                               // Read the file\r
-                               VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);\r
-                               Object value = va.getValue(valueBinding);                               \r
-                               result[i] = value;\r
-                       }\r
-                       return result;\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               }                       \r
-       }\r
-\r
-       /**\r
-        * \r
-        * @param keyBinding\r
-        * @param key\r
-        * @param valueBinding\r
-        * @param value\r
-        * @return true if new file was created\r
-        * @throws AccessorException\r
-        */\r
-       private boolean putLocal(Binding keyBinding, Object key, Binding valueBinding,\r
-                       Object value) throws AccessorException {\r
-               try {\r
-                       // Write\r
-                       String _key = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);\r
-                       File file = new File(path, _key+".dbb");\r
-                       boolean created = !dir.files().contains(file);\r
-                       FileVariantAccessor va = files.createFile( file );\r
-                       va.setValue(valueBinding, value);\r
-                       va.flush();\r
-                       if (created) {\r
-                               dir.add(file);\r
-                       }\r
-                                               \r
-                       // Key variant\r
-                       MutableVariant kv = new MutableVariant(KEY_BINDING, _key);\r
-                       \r
-                       // Notify Listeners\r
-                       if (listeners!=null) {\r
-                               ListenerEntry le = listeners;\r
-                               while (le!=null) {                              \r
-                                       MapInterestSet is = le.getInterestSet();\r
-                                       if (is.inNotificationsOf(kv)) {\r
-                                               \r
-                                               MutableVariant vv = null;\r
-                                               if (is.inValuesOf(kv)) vv = new MutableVariant(valueBinding, valueBinding.isImmutable() ? value : valueBinding.clone(value));\r
-                                               \r
-                                               if (!created) {\r
-                                                       // Notify about new assignment to old value\r
-                                                       ValueAssigned e = new ValueAssigned( new KeyReference(kv), vv);\r
-                                                       emitEvent(le, e);\r
-                                               } else {\r
-                                                       // Notify about new entry\r
-                                                       MapEntryAdded e = new MapEntryAdded(kv, vv);\r
-                                                       emitEvent(le, e);\r
-                                               }\r
-                                       }\r
-                                       \r
-                                       le = le.next;\r
-                               }\r
-                       }\r
-                       return created; \r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               }               \r
-       }\r
-       \r
-       @Override\r
-       public void put(Binding keyBinding, Object key, Binding valueBinding,\r
-                       Object value) throws AccessorException {\r
-               /*boolean created =*/ putLocal(keyBinding, key, valueBinding, value);\r
-       }\r
-\r
-       @Override\r
-       public void putAll(Binding keyBinding, Binding valueBinding,\r
-                       Map<Object, Object> from) throws AccessorException {\r
-               //boolean created = false;\r
-               for (Entry<Object, Object> e : from.entrySet()) {\r
-                       Object key = e.getKey();\r
-                       Object value = e.getValue();\r
-                       /*created |=*/ putLocal(keyBinding, key, valueBinding, value);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public void putAll(Binding keyBinding, Binding valueBinding, Object[] keys,\r
-                       Object[] values) throws AccessorException {\r
-               //boolean created = false;\r
-               if (keys.length!=values.length)\r
-                       throw new AccessorException("Array lengths mismatch");\r
-               for (int i=0; i<keys.length; i++) {\r
-                       Object key = keys[i];\r
-                       Object value = values[i];\r
-                       /*created |=*/ putLocal(keyBinding, key, valueBinding, value);\r
-               }\r
-       }       \r
-\r
-       @Override\r
-       public void remove(Binding keyBinding, Object key) throws AccessorException {\r
-\r
-               try {\r
-                       String key_ = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);\r
-                       File file = new File(path, key_ + ".dbb" );\r
-                       if (!dir.files().contains(file)) return;\r
-//                             throw new AccessorException("File "+file+" does not exist");\r
-                                               \r
-                       files.expunge();\r
-                       \r
-                       boolean deleteOk = files.deleteFile(file);\r
-                       \r
-                       if (!deleteOk) {\r
-                               throw new AccessorException("Failed to delete "+file);\r
-                       } else {\r
-                               dir.remove(file);\r
-                       }\r
-                       \r
-                       // Notify Listeners\r
-                       if (listeners!=null) {\r
-                               MutableVariant var = new MutableVariant(KEY_BINDING, key_);\r
-                               ListenerEntry le = listeners;\r
-                               while (le!=null) {                              \r
-                                       MapInterestSet is = le.getInterestSet();                                \r
-                                       if (is.inNotificationsOf(var)) {\r
-                                               MapEntryRemoved e = new MapEntryRemoved(var); \r
-                                               emitEvent(le, e);\r
-                                       }                                                                       \r
-                                       le = le.next;\r
-                               }               \r
-                       }\r
-                       \r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               }       \r
-               \r
-       }\r
-\r
-       public boolean setValue(ChildReference path, Binding binding, Object obj) throws AccessorException {\r
-               try {\r
-                       Accessor a = getComponent(path);\r
-                       a.setValue(binding, obj);\r
-                       return true;\r
-               } catch (ReferenceException re) {\r
-                       return false;\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-       }\r
-       \r
-       @Override\r
-       public void setValue(Binding mapBinding, Object newMap)\r
-                       throws AccessorException {\r
-               try {\r
-                       MapBinding mb = (MapBinding) mapBinding;\r
-                       Binding valueBinding = mb.getValueBinding();\r
-                       int size = mb.size(newMap);\r
-                       Object keys[] = new Object[size];\r
-                       Object values[] = new Object[size];\r
-                       mb.getAll(newMap, keys, values);\r
-                       \r
-                       Adapter keyAdapter = params.adapterScheme.getAdapter(mb.getKeyBinding(), KEY_BINDING, true, false);\r
-                       HashSet<File> writeFiles = new HashSet<File>();\r
-                       List<File> oldFiles = dir.files();\r
-                       \r
-                       //HashSet<File> modifiedFiles = new HashSet<File>();\r
-                       HashSet<File> addedFiles = new HashSet<File>(writeFiles);\r
-                       addedFiles.removeAll(oldFiles);\r
-                       \r
-                       // Write\r
-                       for (int i=0; i<keys.length; i++) {\r
-                               Object key = keys[i];\r
-                               Object value = values[i];\r
-                               String _key = (String) keyAdapter.adapt(key);\r
-                               String filename = _key + ".dbb";\r
-                               File file = new File(path, filename);\r
-                               writeFiles.add(file);\r
-                                                               \r
-                               boolean existed = oldFiles.contains(file);                      \r
-                               FileVariantAccessor va = files.createFile(file);\r
-                               \r
-                               va.setValue(mb.getValueBinding(), value);\r
-                               va.flush();\r
-                               \r
-                               // Key variant\r
-                               MutableVariant kv = new MutableVariant(KEY_BINDING, _key);\r
-                               \r
-                               // Notify Listeners\r
-                               if (listeners!=null) {\r
-                                       ListenerEntry le = listeners;\r
-                                       while (le!=null) {                              \r
-                                               MapInterestSet is = le.getInterestSet();\r
-                                               if (is.inNotificationsOf(kv)) {\r
-                                                       \r
-                                                       MutableVariant vv = null;\r
-                                                       if (is.inValuesOf(kv)) vv = new MutableVariant(valueBinding, valueBinding.isImmutable() ? value : valueBinding.clone(value));\r
-                                                       \r
-                                                       if (existed) {\r
-                                                               // Notify about new assignment to old value\r
-                                                               ValueAssigned e = new ValueAssigned( new KeyReference(kv), vv);\r
-                                                               emitEvent(le, e);\r
-                                                       } else {\r
-                                                               // Notify about new entry\r
-                                                               MapEntryAdded e = new MapEntryAdded(kv, vv);\r
-                                                               emitEvent(le, e);\r
-                                                       }\r
-                                               }\r
-                                               \r
-                                               le = le.next;\r
-                                       }\r
-                               }\r
-                       }\r
-\r
-                       HashSet<File> removedFiles = new HashSet<File>(oldFiles);\r
-                       removedFiles.removeAll(writeFiles);\r
-                       \r
-                       // Remove old files\r
-                       files.expunge();\r
-                       if (!removedFiles.isEmpty()) {\r
-                               List<File> failList = new ArrayList<File>();\r
-                               for (File f : removedFiles) {\r
-                                       \r
-                                       String filename = f.getName();\r
-                                       String keyStr = filename.substring(0, filename.length()-4);                                     \r
-                                       \r
-                                       boolean deleted = files.deleteFile(f);\r
-                                       if ( !deleted ) {\r
-                                               failList.add(f);\r
-                                       } else {\r
-                                               \r
-                                               // Notify Listeners\r
-                                               if (listeners!=null) {\r
-                                                       MutableVariant var = new MutableVariant(KEY_BINDING, keyStr);\r
-                                                       ListenerEntry le = listeners;\r
-                                                       while (le!=null) {                              \r
-                                                               MapInterestSet is = le.getInterestSet();                                \r
-                                                               if (is.inNotificationsOf(var)) {\r
-                                                                       MapEntryRemoved e = new MapEntryRemoved(var); \r
-                                                                       emitEvent(le, e);\r
-                                                               }                                                                       \r
-                                                               le = le.next;\r
-                                                       }               \r
-                                               }                                       \r
-                                       }\r
-                                       \r
-                                       if (!failList.isEmpty()) {\r
-                                               StringBuilder sb = new StringBuilder();\r
-                                               sb.append("Failed to delete");\r
-                                               for (File ff : failList)  {\r
-                                                       sb.append(' ');\r
-                                                       sb.append(ff.toString());\r
-                                               }\r
-                                               throw new AccessorException(sb.toString());\r
-                                       }\r
-                               }\r
-                       }\r
-                       dir.refresh();\r
-                                               \r
-               } catch (BindingException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AdaptException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AdapterConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               }               \r
-       }\r
-\r
-       @Override\r
-       public int size() throws AccessorException {\r
-               dir.refresh();\r
-               return dir.files().size();\r
-       }\r
-\r
-       @Override       \r
-       public void addListener(Listener listener, InterestSet interestSet, ChildReference path, Executor executor) throws AccessorException {\r
-               listeners = ListenerEntry.link(listeners, listener, interestSet, path, executor);               \r
-               MapInterestSet is = (MapInterestSet) interestSet;\r
-                               \r
-               try {\r
-                       for (File f : dir.files()) {\r
-                               String filename = f.getName();\r
-                               String keyStr = filename.substring(0, filename.length()-4);\r
-                               \r
-                               Accessor sa = getExistingAccessor(KEY_BINDING, keyStr);\r
-                               if (sa==null) continue;\r
-                               \r
-                               MutableVariant key = new MutableVariant(KEY_BINDING, keyStr);\r
-                               InterestSet cis = is.getComponentInterest();\r
-                               if (cis!=null) { \r
-                                       ChildReference childPath = ChildReference.concatenate( path, new KeyReference(key) );\r
-                                       sa.addListener(listener, cis, childPath, executor);                             \r
-                               }\r
-                               cis = is.getComponentInterest( key );\r
-                               if (cis!=null) {\r
-                                       ChildReference childPath = ChildReference.concatenate( path, new KeyReference(key) );\r
-                                       sa.addListener(listener, cis, childPath, executor);                             \r
-                               }\r
-                       }       \r
-               } catch (AccessorConstructionException e) {\r
-                       throw new AccessorException(e);\r
-               }\r
-                       \r
-       }\r
-\r
-       @Override\r
-       public void apply(List<Event> cs, LinkedList<Event> rollback) throws AccessorException {\r
-               try {\r
-                       boolean makeRollback = rollback != null;\r
-                       ArrayList<Event> single = new ArrayList<Event>();\r
-                       for (Event e : cs) {\r
-                               if (e.reference==null) {\r
-                                       Event rbe = applyLocal(e, makeRollback);\r
-                                       if (makeRollback) {\r
-                                               rbe.reference = e.reference;\r
-                                               rollback.addFirst( rbe );\r
-                                       }                                       \r
-                               } else {\r
-                                       Accessor sa = getComponent(e.reference);\r
-                                       // Apply changes\r
-                                       single.clear();\r
-                                       Event noRefEvent = e.clone(null);\r
-                                       single.add(noRefEvent);\r
-                                       sa.apply(single, rollback);\r
-                               }\r
-                       }\r
-               } catch (AccessorConstructionException ae) {\r
-                       throw new AccessorException(ae);\r
-               }               \r
-       }\r
-       \r
-       Event applyLocal(Event e, boolean makeRollback) throws AccessorException {\r
-               Event rollback = null;\r
-               try {\r
-                       if (e instanceof ValueAssigned) {\r
-                               ValueAssigned va = (ValueAssigned) e;\r
-                               if (makeRollback) {\r
-                                       Binding binding = params.bindingScheme.getBinding(type());\r
-                                       rollback = new ValueAssigned(binding, getValue(binding));                               \r
-                               }\r
-                               setValue(va.newValue.getBinding(), va.newValue.getValue());\r
-                               return rollback;\r
-                       } else if (e instanceof MapEntryAdded) {\r
-                               MapEntryAdded ea = (MapEntryAdded) e;\r
-                               if (ea.key==null) throw new AccessorException("Cannot apply entry added event because key is missing");\r
-                               if (ea.value==null) throw new AccessorException("Cannot apply entry added event because value is missing");\r
-                               boolean hadValue = containsKey(ea.key.getBinding(), ea.key.getValue());\r
-                               if (hadValue) throw new AccessorException("Could not add entry to key that already existed");\r
-                               \r
-                               if (makeRollback) {                             \r
-                                       rollback = new MapEntryRemoved( ea.key );\r
-                               }\r
-                               \r
-                               put(ea.key.getBinding(), ea.key.getValue(), ea.value.getBinding(), ea.value.getValue());\r
-                               \r
-                       } else if (e instanceof MapEntryRemoved) {\r
-                               MapEntryRemoved er = (MapEntryRemoved) e;\r
-                               \r
-                               if (makeRollback) {\r
-                                       boolean hadValue = containsKey(er.key.getBinding(), er.key.getValue());\r
-                                       \r
-                                       if (hadValue) {                         \r
-                                               MutableVariant oldKey = er.key;\r
-                                               MutableVariant oldValue = getAsVariant(er.key.getBinding(), er.key.getValue());\r
-                                               rollback = new MapEntryAdded(oldKey, oldValue);\r
-                                       } else {\r
-                                               rollback = new MapEntryRemoved( er.key.clone() );\r
-                                       }\r
-                               }\r
-                               \r
-                               remove( er.key.getBinding(), er.key.getValue() );\r
-                               \r
-                       } else throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Map Type");\r
-                       \r
-                       return rollback;\r
-               } catch (BindingConstructionException e2) {\r
-                       throw new AccessorException( e2 );\r
-               }\r
-       }\r
-       \r
-\r
-       @SuppressWarnings("unchecked")\r
-       @Override\r
-       public <T extends Accessor> T getComponent(ChildReference reference)\r
-                       throws AccessorConstructionException {\r
-               if (reference==null) return (T) this;\r
-               if (reference instanceof LabelReference) {\r
-                       try {\r
-                               LabelReference lr = (LabelReference) reference;\r
-                                                               \r
-                               MutableVariant variant = (MutableVariant) params.adapterScheme.adapt(lr.label, Bindings.STRING, Bindings.MUTABLE_VARIANT);\r
-                               Object value = variant.getValue(KEY_BINDING);                           \r
-                               Accessor result = (T) getValueAccessor(KEY_BINDING, value);\r
-                               \r
-                               if (reference.getChildReference() != null)\r
-                                       result = result.getComponent(reference.getChildReference());\r
-                               return (T) result;\r
-                       } catch (AdaptException e) {\r
-                               throw new ReferenceException(e);\r
-                       }\r
-               } else if (reference instanceof KeyReference) {\r
-                       try {\r
-                               KeyReference ref = (KeyReference) reference;                    \r
-                               String keyStr = (String) params.adapterScheme.adapt(ref.key.getValue(), ref.key.getBinding(), KEY_BINDING);                                             \r
-                               File f = keyToFile(keyStr);\r
-                               if (!dir.files().contains(f))\r
-                                       throw new AccessorConstructionException("Invalid reference "+ref.key);\r
-       \r
-                               Accessor result = getValueAccessor(KEY_BINDING, keyStr);\r
-                               if (reference.getChildReference() != null)\r
-                                       result = result.getComponent(reference.getChildReference());\r
-                               return (T) result;\r
-                       } catch (AdaptException e) {\r
-                               throw new ReferenceException(e);\r
-                       }\r
-               } \r
-               throw new ReferenceException(reference.getClass().getName()+" is not a reference of a map");    \r
-       }\r
-\r
-       @Override\r
-       public void removeListener(Listener listener) throws AccessorException {\r
-               detachListener(listener);\r
-       }\r
-       \r
-       protected ListenerEntry detachListener(Listener listener) throws AccessorException {\r
-               ListenerEntry e = listeners;\r
-               ListenerEntry p = null;\r
-               while (e!=null) {\r
-                       // Found match\r
-                       if (e.listener == listener) {\r
-                               // The match was the first entry of the linked list\r
-                               if (p==null) {\r
-                                       listeners = e.next;\r
-                                       return e;\r
-                               }\r
-                               // Some other entry, unlink e\r
-                               p.next = e.next;\r
-                               return e;\r
-                       }\r
-                       p = e;\r
-                       e = e.next;\r
-               }\r
-               return null;            \r
-       }\r
-\r
-       protected void emitEvent(ListenerEntry le, Event e) {           \r
-               e.reference = ChildReference.concatenate(le.path, e.reference);\r
-               le.emitEvent(e);\r
-       }       \r
-\r
-       protected void emitEvents(ListenerEntry le, Collection<Event> events) {\r
-               for (Event e : events)\r
-                       e.reference = ChildReference.concatenate(le.path, e.reference);\r
-               le.emitEvents(events);\r
-       }       \r
-       \r
-       \r
-}\r
-\r
+/*******************************************************************************
+ * Industry THTH ry.
+ * Copyright (c) 2010- Association for Decentralized Information Management in
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *    VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.databoard.accessor.impl;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.Executor;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.Datatypes;
+import org.simantics.databoard.accessor.Accessor;
+import org.simantics.databoard.accessor.CloseableAccessor;
+import org.simantics.databoard.accessor.MapAccessor;
+import org.simantics.databoard.accessor.VariantAccessor;
+import org.simantics.databoard.accessor.error.AccessorConstructionException;
+import org.simantics.databoard.accessor.error.AccessorException;
+import org.simantics.databoard.accessor.error.ReferenceException;
+import org.simantics.databoard.accessor.event.Event;
+import org.simantics.databoard.accessor.event.MapEntryAdded;
+import org.simantics.databoard.accessor.event.MapEntryRemoved;
+import org.simantics.databoard.accessor.event.ValueAssigned;
+import org.simantics.databoard.accessor.file.FileLibrary;
+import org.simantics.databoard.accessor.file.FileVariantAccessor;
+import org.simantics.databoard.accessor.impl.DirectoryWatch.DirectoryEvent;
+import org.simantics.databoard.accessor.impl.DirectoryWatch.DirectoryListener;
+import org.simantics.databoard.accessor.interestset.InterestSet;
+import org.simantics.databoard.accessor.interestset.MapInterestSet;
+import org.simantics.databoard.accessor.reference.ChildReference;
+import org.simantics.databoard.accessor.reference.KeyReference;
+import org.simantics.databoard.accessor.reference.LabelReference;
+import org.simantics.databoard.adapter.AdaptException;
+import org.simantics.databoard.adapter.Adapter;
+import org.simantics.databoard.adapter.AdapterConstructionException;
+import org.simantics.databoard.binding.ArrayBinding;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.binding.MapBinding;
+import org.simantics.databoard.binding.VariantBinding;
+import org.simantics.databoard.binding.error.BindingConstructionException;
+import org.simantics.databoard.binding.error.BindingException;
+import org.simantics.databoard.binding.error.RuntimeBindingException;
+import org.simantics.databoard.binding.mutable.MutableVariant;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.type.MapType;
+
+/**
+ * DirectoryMap is a file backed map implementation where keys are filenames
+ * and values are corresponding files. 
+ * <p>
+ * This class is an implmentation to Map(Variant, Variant) -Accessor.
+ * 
+ *  Filenames have the following encoding:
+ *    S<string>.dbb   String types, if string doesn't have the following 
+ *                    control characters " : < > | ? * \ /   [0..31]
+ *    I<integer>.dbb  Integer types
+ *    L<long>.dbb     Long types
+ *    H<hex>.dbb      All other cases the value as binary 
+ * <p>
+ * File accessor is created if an entry opened as a sub-accessor.
+ * The file accessor is closed when all sub-accessors are released.
+ * The implementation is based on proxy instances and a reference queue.
+ * Once the queue is empty, file accessor is closed.
+ * <p>
+ * DirectoryMap must be closed with #close();     
+ *
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public class DirectoryMap implements MapAccessor, CloseableAccessor {
+
+       /** Key binding */
+       final static Binding KEY_BINDING = Bindings.STR_VARIANT;
+       
+       /** Cache of sub-accessors */
+       FileLibrary files;
+       
+       /** Monitors directory for file changes */
+       DirectoryWatch dir;
+       
+       /** Folder */
+       File path;
+       
+       /** Listeners */
+       ListenerEntry listeners = null;
+       
+       /** Parent, optional */
+       Accessor parent; 
+
+       /** Accessor params */
+       AccessorParams params;
+       
+       DirectoryListener dirListener = new DirectoryListener() {
+               @Override
+               public void onWatchEvent(DirectoryEvent e) {
+                       
+               }
+       };
+
+       public DirectoryMap(File directory) {
+               this(directory, null, AccessorParams.DEFAULT);
+       }
+
+       public DirectoryMap(File directory, Accessor parent) {
+               this(directory, parent, AccessorParams.DEFAULT);
+       }
+
+       public DirectoryMap(File directory, Accessor parent, AccessorParams params) {
+               this.parent = parent;
+               this.path = directory;
+               this.params = params;
+
+               // Filters .dbb files
+               FileFilter filter = new FileFilter() {
+                       public boolean accept(File pathname) {                          
+                               String filename = pathname.getName();
+                               if (filename.length()==0) return false;
+                               char c = filename.charAt(0);
+                               if (c!='S' && c!='I' && c!='L' && c!='B') return false;
+                               if (filename.endsWith(".dbb")) return true;
+                               return filename.toLowerCase().endsWith(".dbb");
+                       }};
+               
+               dir = new DirectoryWatch(path, filter);
+               
+               dir.addListener( dirListener );
+               
+               files = new FileLibrary();
+       }
+       
+       public void close() {
+               dir.removeListener( dirListener );
+               dir.close();
+               files.close();
+       }
+       
+       static MapType type = new MapType(Datatypes.VARIANT, Datatypes.VARIANT);
+       public MapType type() {
+               return type;
+       }
+
+       private String fileToKey(File f) {
+               String filename = f.getName();
+               String keyStr = filename.substring(0, filename.length()-4);
+               return keyStr;
+       }
+       
+       private File keyToFile(String keyStr) {
+               return new File(path, keyStr + ".dbb" );
+       }
+       
+       @Override
+       public void clear() throws AccessorException {
+               //List<File> deleteList = dir.files();
+               List<File> failList = new ArrayList<File>();
+               boolean hasListeners = listeners!=null;
+               List<String> keys = hasListeners ? new ArrayList<String>() : null;
+               
+               // Close all file handles
+               files.close();
+               
+               // Delete files
+               for (File f : dir.files()) {
+                       if (!files.deleteFile(f)) {
+                               failList.add(f);
+                       }
+                       if (hasListeners) {
+                               String keyStr = fileToKey(f);
+                               keys.add(keyStr);
+                       }
+               }
+
+               // Re-read directory
+               dir.refresh();
+               
+               // Notify Listeners
+               ListenerEntry le = listeners;
+               while (le!=null) {                              
+                       MapInterestSet is = le.getInterestSet();
+                       for (Object key : keys) {
+                               MutableVariant var = new MutableVariant(KEY_BINDING, key);
+                               if (is.inNotificationsOf(var)) {
+                                       MapEntryRemoved e = new MapEntryRemoved(var); 
+                                       emitEvent(le, e);
+                               }
+                       }                                                       
+                       le = le.next;
+               }                               
+               
+               // Some files failed to delete
+               if (!failList.isEmpty()) {
+                       StringBuilder sb = new StringBuilder();
+                       sb.append("Failed to delete");
+                       for (File f : failList)  {
+                               sb.append(' ');
+                               sb.append(f.toString());
+                       }
+                       // HAX
+                       throw new AccessorException(sb.toString());
+               }
+               
+                               
+       }
+               
+       @Override
+       public String toString() {
+               return dir.toString();
+       }
+       
+       public Object getValue(Binding binding) throws AccessorException {
+               MapBinding mb = (MapBinding) binding; 
+               if (mb.getKeyBinding() instanceof VariantBinding==false || mb.getValueBinding() instanceof VariantBinding==false)
+                       throw new AccessorException("Map(Variant, Variant) Expected");
+               // Get all files as a single map
+               try {
+                       Object result = binding.createDefault();
+                       for (File f : dir.files()) {
+                               // Create Key
+                               String keyStr = fileToKey(f);
+                               Object key = params.adapterScheme.adapt(keyStr, KEY_BINDING, mb.getKeyBinding());
+                               
+                               // Read value
+                               VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);
+                               Object value = va.getValue(mb.getValueBinding());
+                               
+                               mb.put(result, key, value);
+                       }
+                       return result;
+               } catch (BindingException e) {
+                       throw new AccessorException(e);
+               } catch (AccessorConstructionException e) {
+                       throw new AccessorException(e);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               }
+       }
+       
+       @Override
+       public void getValue(Binding dstBinding, Object dst) throws AccessorException {
+               MapBinding db = (MapBinding) dstBinding; 
+               Binding dkb = db.getKeyBinding();
+               Binding dvb = db.getValueBinding();
+               if (dkb instanceof VariantBinding==false || dvb instanceof VariantBinding==false)
+                       throw new AccessorException("Map(Variant, Variant) Expected");
+               // Get all files as a single map
+               try {
+                       TreeSet<Object> dstKeys = new TreeSet<Object>(dkb);
+                       db.getKeys(dst, dstKeys);
+                       
+                       for (File f : dir.files()) {
+                               // Create Key
+                               String keyStr = fileToKey(f);
+                               Object key = params.adapterScheme.adapt(keyStr, KEY_BINDING, dkb);
+                               
+                               Object v = db.containsKey(dst, key) ? db.get(dst, key) : dvb.createDefault();
+                               VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);
+                               va.getValue(dvb, v);
+                               
+                               db.put(dst, key, v);
+                               dstKeys.remove(key);
+                       }
+                       
+                       for (Object key : dstKeys)
+                               db.remove(dst, key);
+               } catch (BindingException e) {
+                       throw new AccessorException(e);
+               } catch (AccessorConstructionException e) {
+                       throw new AccessorException(e);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               }
+               
+       }
+       
+       @Override
+       public boolean getValue(ChildReference path, Binding binding, Object obj) throws AccessorException {
+               try {
+                       Accessor a = getComponent(path);
+                       a.getValue(binding, obj);
+                       return true;
+               } catch (ReferenceException re) {
+                       return false;
+               } catch (AccessorConstructionException e) {
+                       throw new AccessorException(e);
+               }
+       }       
+       
+       public Object getValue(ChildReference path, Binding binding) throws AccessorException {
+               try {
+                       Accessor a = getComponent(path);
+                       return a.getValue(binding);
+               } catch (ReferenceException re) {
+                       return null;
+               } catch (AccessorConstructionException e) {
+                       throw new AccessorException(e);
+               }
+       }
+       
+       
+       @Override
+       public boolean containsKey(Binding keyBinding, Object key)
+                       throws AccessorException {
+               try {
+                       String key_ = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
+                       File file = keyToFile(key_);
+                       return dir.files().contains(file);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               }
+       }
+
+       @Override
+       public boolean containsValue(Binding valueBinding, Object value)
+                       throws AccessorException {
+               try {
+                       for (File f : dir.files()) {
+                               String key = fileToKey(f);
+                               VariantAccessor va = getValueAccessor(KEY_BINDING, key);
+                               Object v = va.getValue(valueBinding);
+                               boolean match = valueBinding.equals(v, value);
+                               if ( match ) return true;
+                       }
+                       return false;
+               } catch (AccessorConstructionException e) {
+                       throw new AccessorException(e);
+               }               
+       }
+
+       @Override
+       public Object get(Binding keyBinding, Object key, Binding valueBinding)
+                       throws AccessorException {
+               try {
+                       VariantAccessor va = getValueAccessor(keyBinding, key);
+                       return va.getValue(valueBinding);
+               } catch (AccessorConstructionException e) {
+                       throw new AccessorException(e);
+               }
+       }
+
+       /**
+        * Get the value as a variant
+        *  
+        * @param keyBinding
+        * @param key
+        * @return value
+        * @throws AccessorException
+        */
+       public MutableVariant getAsVariant(Binding keyBinding, Object key)
+                       throws AccessorException {
+               try {
+                       VariantAccessor va = getValueAccessor(keyBinding, key);
+                       Datatype type = va.getContentType();
+                       Binding binding = params.bindingScheme.getBinding(type);
+                       Object value = va.getContentValue(binding);
+                       MutableVariant result = new MutableVariant(binding, value);
+                       return result;
+               } catch (AccessorConstructionException e) {
+                       throw new AccessorException(e);
+               } catch (BindingConstructionException e) {
+                       throw new AccessorException(e);
+               }
+       }
+
+       @Override
+       public void getAll(Binding keyBinding, Binding valueBinding,
+                       Map<Object, Object> to) throws AccessorException {
+               try {
+                       for (File f : dir.files()) {    
+                               // Create key
+                               String keyStr = fileToKey(f);
+                               Object key = params.adapterScheme.adapt(keyStr, KEY_BINDING, keyBinding);
+
+                               // Read value
+                               VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);
+                               Object value = va.getValue(valueBinding);
+                               
+                               to.put(key, value);                             
+                       }
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               } catch (AccessorConstructionException e) {
+                       throw new AccessorException(e);
+               }                       
+       }
+
+       @Override
+       public void getAll(Binding keyBinding, Binding valueBinding, Object[] keys,
+                       Object[] values) throws AccessorException {
+               try {
+                       Set<String> fileKeys = createKeys();
+                       
+                       int i=0;
+                       for (String keyStr : fileKeys) {                                
+                               // Read value
+                               VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);
+                               Object value = va.getValue(valueBinding);
+                               
+                               Object key2 = params.adapterScheme.adapt(keyStr, KEY_BINDING, keyBinding);
+                               keys[i] = key2;
+                               values[i] = value;
+                               i++;
+                       }
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               } catch (AccessorConstructionException e) {
+                       throw new AccessorException(e);
+               }                       
+       }
+       
+
+       @Override
+       public int count(Binding keyBinding, Object from,
+                       boolean fromInclusive, Object end, boolean endInclusive)
+                       throws AccessorException {
+               throw new AccessorException("Not implemented");
+       }
+
+       @Override
+       public int getEntries(Binding keyBinding, Object from,
+                       boolean fromInclusive, Object end, boolean endInclusive,
+                       ArrayBinding keyArrayBinding, Object dstKeys,
+                       ArrayBinding valueArrayBinding, Object dstValues, int limit)
+                       throws AccessorException {
+               throw new AccessorException("Not implemented");
+       }
+       
+
+       TreeSet<String> createKeys() throws RuntimeBindingException {
+               List<File> files = dir.files();
+               TreeSet<String> keys = new TreeSet<String>(KEY_BINDING);
+               
+               for (File f : files) {
+                       String filename = f.getName();
+                       String str = filename.substring(0, filename.length()-4);
+                       keys.add(str);
+               }
+               
+               return keys;
+       }
+       
+       @Override
+       public Object getCeilingKey(Binding keyBinding, Object key)
+                       throws AccessorException {
+               try {
+                       TreeSet<String> keys = createKeys();
+                       String k = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
+                       if (keys.contains(k)) return key;
+                       Object res = keys.ceiling(k);
+                       if (res==null) return null;
+                       return params.adapterScheme.adapt(res, KEY_BINDING, keyBinding);
+               } catch (RuntimeBindingException e) {
+                       throw new AccessorException(e);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               }
+       }
+
+       @Override
+       public Object getFirstKey(Binding keyBinding) throws AccessorException {
+               List<File> files = dir.files();
+               String firstKey = null;
+               
+               for (File f : files) {
+                       String filename = f.getName();
+                       String str = filename.substring(0, filename.length()-4);
+                       if (firstKey == null) {
+                               firstKey = str;
+                       } else {
+                               if (KEY_BINDING.compare(str, firstKey)<0) firstKey = str;
+                       }
+               }
+               if (firstKey==null) return null;
+               
+               try {
+                       return params.adapterScheme.adapt(firstKey, KEY_BINDING, keyBinding);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               }
+       }
+
+       @Override
+       public Object getFloorKey(Binding keyBinding, Object key)
+                       throws AccessorException {
+               try {
+                       TreeSet<String> keys = createKeys();
+                       String k = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
+                       Object res = keys.floor(k);
+                       if (res==null) return null;
+                       return params.adapterScheme.adapt(res, KEY_BINDING, keyBinding);
+               } catch (RuntimeBindingException e) {
+                       throw new AccessorException(e);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               }
+       }
+
+       @Override
+       public Object getHigherKey(Binding keyBinding, Object key)
+                       throws AccessorException {
+               try {
+                       TreeSet<String> keys = createKeys();
+                       String k = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
+                       Object res = keys.higher(k);
+                       if (res==null) return null;
+                       return params.adapterScheme.adapt(res, KEY_BINDING, keyBinding);
+               } catch (RuntimeBindingException e) {
+                       throw new AccessorException(e);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               }
+       }
+
+       @Override
+       public Object[] getKeys(Binding keyBinding) throws AccessorException {
+               TreeSet<String> keys = createKeys();
+               Object[] result = new Object[keys.size()];
+               if (keys.isEmpty()) return result;
+               try {
+                       Adapter a = params.adapterScheme.getAdapter(KEY_BINDING, keyBinding, true, false);
+                       int index = 0;
+                       for (String key : keys) {                                                               
+                               result[index++] = a.adapt( key );
+                       }
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               } catch (AdapterConstructionException e) {
+                       throw new AccessorException(e);
+               }
+               
+               return result;
+       }
+
+       @Override
+       public Object getLastKey(Binding keyBinding) throws AccessorException {
+               List<File> files = dir.files();
+               String lastKey = null;
+               
+               for (File f : files) {
+                       String filename = f.getName();
+                       String str = filename.substring(0, filename.length()-4);
+                       if (lastKey == null) {
+                               lastKey = str;
+                       } else {
+                               if (KEY_BINDING.compare(str, lastKey)>0) lastKey = str;
+                       }
+               }
+               if (lastKey==null) return null;
+               
+               try {
+                       return params.adapterScheme.adapt(lastKey, KEY_BINDING, keyBinding);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               }
+       }
+
+       @Override
+       public Object getLowerKey(Binding keyBinding, Object key)
+                       throws AccessorException {
+               try {
+                       TreeSet<String> keys = createKeys();
+                       String k = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
+                       Object res = keys.lower(k);
+                       if (res==null) return null;
+                       return params.adapterScheme.adapt(res, KEY_BINDING, keyBinding);
+               } catch (RuntimeBindingException e) {
+                       throw new AccessorException(e);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               }
+       }
+
+       public FileVariantAccessor getExistingAccessor(Binding keyBinding,
+                       Object key) throws AccessorConstructionException {
+               try {
+                       String key_ = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
+                       File file = new File(path, key_ + ".dbb" );
+                       return files.getExistingFile(file);
+               } catch (AdaptException e) {
+                       throw new AccessorConstructionException(e);
+               }       
+       }
+       
+       @SuppressWarnings("unchecked")
+       @Override
+       public FileVariantAccessor getValueAccessor(Binding keyBinding, Object key) throws AccessorConstructionException {
+               try {
+                       String keyStr = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
+                       File file = keyToFile(keyStr);
+                       FileVariantAccessor sa = files.getExistingFile(file);
+                       if (sa!=null) return sa;
+                       
+                       // Create new accessor
+                       sa = files.getFile(file);
+
+                       // Add component interest sets
+                       ListenerEntry le = listeners;
+                       if (le!=null) {
+                               MutableVariant kv = new MutableVariant(keyBinding, key);
+                               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 e) {
+                                                       throw new AccessorConstructionException(e);
+                                               }
+                                       }
+                                               
+                                       // 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 e) {
+                                                       throw new AccessorConstructionException(e);
+                                               }
+                                       }
+                                       
+                                       // Next listener
+                                       le = le.next;
+                               }
+                       }
+                       
+                       return sa;
+               } catch (AdaptException e) {
+                       throw new AccessorConstructionException(e);
+               }       
+       }
+
+       @Override
+       public Object[] getValues(Binding valueBinding) throws AccessorException {              
+               try {
+                       Set<String> keys = createKeys();
+                       int count = keys.size();
+                       Object[] result = new Object[ count ];
+                       
+                       Iterator<String> iter = keys.iterator();
+                       for (int i=0; i<count; i++) {
+                               String keyStr = iter.next();
+                               // Read the file
+                               VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);
+                               Object value = va.getValue(valueBinding);                               
+                               result[i] = value;
+                       }
+                       return result;
+               } catch (AccessorConstructionException e) {
+                       throw new AccessorException(e);
+               }                       
+       }
+
+       /**
+        * 
+        * @param keyBinding
+        * @param key
+        * @param valueBinding
+        * @param value
+        * @return true if new file was created
+        * @throws AccessorException
+        */
+       private boolean putLocal(Binding keyBinding, Object key, Binding valueBinding,
+                       Object value) throws AccessorException {
+               try {
+                       // Write
+                       String _key = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
+                       File file = new File(path, _key+".dbb");
+                       boolean created = !dir.files().contains(file);
+                       FileVariantAccessor va = files.createFile( file );
+                       va.setValue(valueBinding, value);
+                       va.flush();
+                       if (created) {
+                               dir.add(file);
+                       }
+                                               
+                       // Key variant
+                       MutableVariant kv = new MutableVariant(KEY_BINDING, _key);
+                       
+                       // Notify Listeners
+                       if (listeners!=null) {
+                               ListenerEntry le = listeners;
+                               while (le!=null) {                              
+                                       MapInterestSet is = le.getInterestSet();
+                                       if (is.inNotificationsOf(kv)) {
+                                               
+                                               MutableVariant vv = null;
+                                               if (is.inValuesOf(kv)) vv = new MutableVariant(valueBinding, valueBinding.isImmutable() ? value : valueBinding.clone(value));
+                                               
+                                               if (!created) {
+                                                       // Notify about new assignment to old value
+                                                       ValueAssigned e = new ValueAssigned( new KeyReference(kv), vv);
+                                                       emitEvent(le, e);
+                                               } else {
+                                                       // Notify about new entry
+                                                       MapEntryAdded e = new MapEntryAdded(kv, vv);
+                                                       emitEvent(le, e);
+                                               }
+                                       }
+                                       
+                                       le = le.next;
+                               }
+                       }
+                       return created; 
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               } catch (AccessorConstructionException e) {
+                       throw new AccessorException(e);
+               }               
+       }
+       
+       @Override
+       public void put(Binding keyBinding, Object key, Binding valueBinding,
+                       Object value) throws AccessorException {
+               /*boolean created =*/ putLocal(keyBinding, key, valueBinding, value);
+       }
+
+       @Override
+       public void putAll(Binding keyBinding, Binding valueBinding,
+                       Map<Object, Object> from) throws AccessorException {
+               //boolean created = false;
+               for (Entry<Object, Object> e : from.entrySet()) {
+                       Object key = e.getKey();
+                       Object value = e.getValue();
+                       /*created |=*/ putLocal(keyBinding, key, valueBinding, value);
+               }
+       }
+
+       @Override
+       public void putAll(Binding keyBinding, Binding valueBinding, Object[] keys,
+                       Object[] values) throws AccessorException {
+               //boolean created = false;
+               if (keys.length!=values.length)
+                       throw new AccessorException("Array lengths mismatch");
+               for (int i=0; i<keys.length; i++) {
+                       Object key = keys[i];
+                       Object value = values[i];
+                       /*created |=*/ putLocal(keyBinding, key, valueBinding, value);
+               }
+       }       
+
+       @Override
+       public void remove(Binding keyBinding, Object key) throws AccessorException {
+
+               try {
+                       String key_ = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
+                       File file = new File(path, key_ + ".dbb" );
+                       if (!dir.files().contains(file)) return;
+//                             throw new AccessorException("File "+file+" does not exist");
+                                               
+                       files.expunge();
+                       
+                       boolean deleteOk = files.deleteFile(file);
+                       
+                       if (!deleteOk) {
+                               throw new AccessorException("Failed to delete "+file);
+                       } else {
+                               dir.remove(file);
+                       }
+                       
+                       // Notify Listeners
+                       if (listeners!=null) {
+                               MutableVariant var = new MutableVariant(KEY_BINDING, key_);
+                               ListenerEntry le = listeners;
+                               while (le!=null) {                              
+                                       MapInterestSet is = le.getInterestSet();                                
+                                       if (is.inNotificationsOf(var)) {
+                                               MapEntryRemoved e = new MapEntryRemoved(var); 
+                                               emitEvent(le, e);
+                                       }                                                                       
+                                       le = le.next;
+                               }               
+                       }
+                       
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               }       
+               
+       }
+
+       public boolean setValue(ChildReference path, Binding binding, Object obj) throws AccessorException {
+               try {
+                       Accessor a = getComponent(path);
+                       a.setValue(binding, obj);
+                       return true;
+               } catch (ReferenceException re) {
+                       return false;
+               } catch (AccessorConstructionException e) {
+                       throw new AccessorException(e);
+               }
+       }
+       
+       @Override
+       public void setValue(Binding mapBinding, Object newMap)
+                       throws AccessorException {
+               try {
+                       MapBinding mb = (MapBinding) mapBinding;
+                       Binding valueBinding = mb.getValueBinding();
+                       int size = mb.size(newMap);
+                       Object keys[] = new Object[size];
+                       Object values[] = new Object[size];
+                       mb.getAll(newMap, keys, values);
+                       
+                       Adapter keyAdapter = params.adapterScheme.getAdapter(mb.getKeyBinding(), KEY_BINDING, true, false);
+                       HashSet<File> writeFiles = new HashSet<File>();
+                       List<File> oldFiles = dir.files();
+                       
+                       //HashSet<File> modifiedFiles = new HashSet<File>();
+                       HashSet<File> addedFiles = new HashSet<File>(writeFiles);
+                       addedFiles.removeAll(oldFiles);
+                       
+                       // Write
+                       for (int i=0; i<keys.length; i++) {
+                               Object key = keys[i];
+                               Object value = values[i];
+                               String _key = (String) keyAdapter.adapt(key);
+                               String filename = _key + ".dbb";
+                               File file = new File(path, filename);
+                               writeFiles.add(file);
+                                                               
+                               boolean existed = oldFiles.contains(file);                      
+                               FileVariantAccessor va = files.createFile(file);
+                               
+                               va.setValue(mb.getValueBinding(), value);
+                               va.flush();
+                               
+                               // Key variant
+                               MutableVariant kv = new MutableVariant(KEY_BINDING, _key);
+                               
+                               // Notify Listeners
+                               if (listeners!=null) {
+                                       ListenerEntry le = listeners;
+                                       while (le!=null) {                              
+                                               MapInterestSet is = le.getInterestSet();
+                                               if (is.inNotificationsOf(kv)) {
+                                                       
+                                                       MutableVariant vv = null;
+                                                       if (is.inValuesOf(kv)) vv = new MutableVariant(valueBinding, valueBinding.isImmutable() ? value : valueBinding.clone(value));
+                                                       
+                                                       if (existed) {
+                                                               // Notify about new assignment to old value
+                                                               ValueAssigned e = new ValueAssigned( new KeyReference(kv), vv);
+                                                               emitEvent(le, e);
+                                                       } else {
+                                                               // Notify about new entry
+                                                               MapEntryAdded e = new MapEntryAdded(kv, vv);
+                                                               emitEvent(le, e);
+                                                       }
+                                               }
+                                               
+                                               le = le.next;
+                                       }
+                               }
+                       }
+
+                       HashSet<File> removedFiles = new HashSet<File>(oldFiles);
+                       removedFiles.removeAll(writeFiles);
+                       
+                       // Remove old files
+                       files.expunge();
+                       if (!removedFiles.isEmpty()) {
+                               List<File> failList = new ArrayList<File>();
+                               for (File f : removedFiles) {
+                                       
+                                       String filename = f.getName();
+                                       String keyStr = filename.substring(0, filename.length()-4);                                     
+                                       
+                                       boolean deleted = files.deleteFile(f);
+                                       if ( !deleted ) {
+                                               failList.add(f);
+                                       } else {
+                                               
+                                               // Notify Listeners
+                                               if (listeners!=null) {
+                                                       MutableVariant var = new MutableVariant(KEY_BINDING, keyStr);
+                                                       ListenerEntry le = listeners;
+                                                       while (le!=null) {                              
+                                                               MapInterestSet is = le.getInterestSet();                                
+                                                               if (is.inNotificationsOf(var)) {
+                                                                       MapEntryRemoved e = new MapEntryRemoved(var); 
+                                                                       emitEvent(le, e);
+                                                               }                                                                       
+                                                               le = le.next;
+                                                       }               
+                                               }                                       
+                                       }
+                                       
+                                       if (!failList.isEmpty()) {
+                                               StringBuilder sb = new StringBuilder();
+                                               sb.append("Failed to delete");
+                                               for (File ff : failList)  {
+                                                       sb.append(' ');
+                                                       sb.append(ff.toString());
+                                               }
+                                               throw new AccessorException(sb.toString());
+                                       }
+                               }
+                       }
+                       dir.refresh();
+                                               
+               } catch (BindingException e) {
+                       throw new AccessorException(e);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               } catch (AdapterConstructionException e) {
+                       throw new AccessorException(e);
+               } catch (AccessorConstructionException e) {
+                       throw new AccessorException(e);
+               }               
+       }
+
+       @Override
+       public int size() throws AccessorException {
+               dir.refresh();
+               return dir.files().size();
+       }
+
+       @Override       
+       public void addListener(Listener listener, InterestSet interestSet, ChildReference path, Executor executor) throws AccessorException {
+               listeners = ListenerEntry.link(listeners, listener, interestSet, path, executor);               
+               MapInterestSet is = (MapInterestSet) interestSet;
+                               
+               try {
+                       for (File f : dir.files()) {
+                               String filename = f.getName();
+                               String keyStr = filename.substring(0, filename.length()-4);
+                               
+                               Accessor sa = getExistingAccessor(KEY_BINDING, keyStr);
+                               if (sa==null) continue;
+                               
+                               MutableVariant key = new MutableVariant(KEY_BINDING, keyStr);
+                               InterestSet cis = is.getComponentInterest();
+                               if (cis!=null) { 
+                                       ChildReference childPath = ChildReference.concatenate( path, new KeyReference(key) );
+                                       sa.addListener(listener, cis, childPath, executor);                             
+                               }
+                               cis = is.getComponentInterest( key );
+                               if (cis!=null) {
+                                       ChildReference childPath = ChildReference.concatenate( path, new KeyReference(key) );
+                                       sa.addListener(listener, cis, childPath, executor);                             
+                               }
+                       }       
+               } catch (AccessorConstructionException e) {
+                       throw new AccessorException(e);
+               }
+                       
+       }
+
+       @Override
+       public void apply(List<Event> cs, LinkedList<Event> rollback) throws AccessorException {
+               try {
+                       boolean makeRollback = rollback != null;
+                       ArrayList<Event> single = new ArrayList<Event>();
+                       for (Event e : cs) {
+                               if (e.reference==null) {
+                                       Event rbe = applyLocal(e, makeRollback);
+                                       if (makeRollback) {
+                                               rbe.reference = e.reference;
+                                               rollback.addFirst( rbe );
+                                       }                                       
+                               } else {
+                                       Accessor sa = getComponent(e.reference);
+                                       // Apply changes
+                                       single.clear();
+                                       Event noRefEvent = e.clone(null);
+                                       single.add(noRefEvent);
+                                       sa.apply(single, rollback);
+                               }
+                       }
+               } catch (AccessorConstructionException ae) {
+                       throw new AccessorException(ae);
+               }               
+       }
+       
+       Event applyLocal(Event e, boolean makeRollback) throws AccessorException {
+               Event rollback = null;
+               try {
+                       if (e instanceof ValueAssigned) {
+                               ValueAssigned va = (ValueAssigned) e;
+                               if (makeRollback) {
+                                       Binding binding = params.bindingScheme.getBinding(type());
+                                       rollback = new ValueAssigned(binding, getValue(binding));                               
+                               }
+                               setValue(va.newValue.getBinding(), va.newValue.getValue());
+                               return rollback;
+                       } 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 );
+                               }
+                               
+                               put(ea.key.getBinding(), ea.key.getValue(), ea.value.getBinding(), ea.value.getValue());
+                               
+                       } else if (e instanceof MapEntryRemoved) {
+                               MapEntryRemoved er = (MapEntryRemoved) e;
+                               
+                               if (makeRollback) {
+                                       boolean hadValue = containsKey(er.key.getBinding(), er.key.getValue());
+                                       
+                                       if (hadValue) {                         
+                                               MutableVariant oldKey = er.key;
+                                               MutableVariant oldValue = getAsVariant(er.key.getBinding(), er.key.getValue());
+                                               rollback = new MapEntryAdded(oldKey, oldValue);
+                                       } else {
+                                               rollback = new MapEntryRemoved( er.key.clone() );
+                                       }
+                               }
+                               
+                               remove( er.key.getBinding(), er.key.getValue() );
+                               
+                       } else throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Map Type");
+                       
+                       return rollback;
+               } catch (BindingConstructionException e2) {
+                       throw new AccessorException( e2 );
+               }
+       }
+       
+
+       @SuppressWarnings("unchecked")
+       @Override
+       public <T extends Accessor> T getComponent(ChildReference reference)
+                       throws AccessorConstructionException {
+               if (reference==null) return (T) this;
+               if (reference instanceof LabelReference) {
+                       try {
+                               LabelReference lr = (LabelReference) reference;
+                                                               
+                               MutableVariant variant = (MutableVariant) params.adapterScheme.adapt(lr.label, Bindings.STRING, Bindings.MUTABLE_VARIANT);
+                               Object value = variant.getValue(KEY_BINDING);                           
+                               Accessor result = (T) getValueAccessor(KEY_BINDING, value);
+                               
+                               if (reference.getChildReference() != null)
+                                       result = result.getComponent(reference.getChildReference());
+                               return (T) result;
+                       } catch (AdaptException e) {
+                               throw new ReferenceException(e);
+                       }
+               } else if (reference instanceof KeyReference) {
+                       try {
+                               KeyReference ref = (KeyReference) reference;                    
+                               String keyStr = (String) params.adapterScheme.adapt(ref.key.getValue(), ref.key.getBinding(), KEY_BINDING);                                             
+                               File f = keyToFile(keyStr);
+                               if (!dir.files().contains(f))
+                                       throw new AccessorConstructionException("Invalid reference "+ref.key);
+       
+                               Accessor result = getValueAccessor(KEY_BINDING, keyStr);
+                               if (reference.getChildReference() != null)
+                                       result = result.getComponent(reference.getChildReference());
+                               return (T) result;
+                       } catch (AdaptException e) {
+                               throw new ReferenceException(e);
+                       }
+               } 
+               throw new ReferenceException(reference.getClass().getName()+" is not a reference of a map");    
+       }
+
+       @Override
+       public void removeListener(Listener listener) throws AccessorException {
+               detachListener(listener);
+       }
+       
+       protected ListenerEntry detachListener(Listener listener) throws AccessorException {
+               ListenerEntry e = listeners;
+               ListenerEntry p = null;
+               while (e!=null) {
+                       // Found match
+                       if (e.listener == listener) {
+                               // The match was the first entry of the linked list
+                               if (p==null) {
+                                       listeners = e.next;
+                                       return e;
+                               }
+                               // Some other entry, unlink e
+                               p.next = e.next;
+                               return e;
+                       }
+                       p = e;
+                       e = e.next;
+               }
+               return null;            
+       }
+
+       protected void emitEvent(ListenerEntry le, Event e) {           
+               e.reference = ChildReference.concatenate(le.path, e.reference);
+               le.emitEvent(e);
+       }       
+
+       protected void emitEvents(ListenerEntry le, Collection<Event> events) {
+               for (Event e : events)
+                       e.reference = ChildReference.concatenate(le.path, e.reference);
+               le.emitEvents(events);
+       }       
+       
+       
+}
+