X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.databoard%2Fsrc%2Forg%2Fsimantics%2Fdataboard%2Faccessor%2Fimpl%2FDirectoryMap.java;fp=bundles%2Forg.simantics.databoard%2Fsrc%2Forg%2Fsimantics%2Fdataboard%2Faccessor%2Fimpl%2FDirectoryMap.java;h=5a94b04ca4cd51d3e3ce2b4d8e72696f4477dde5;hp=647aab8239aab6b6c72ec314551069362230c450;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hpb=24e2b34260f219f0d1644ca7a138894980e25b14
diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/impl/DirectoryMap.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/impl/DirectoryMap.java
index 647aab823..5a94b04ca 100644
--- a/bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/impl/DirectoryMap.java
+++ b/bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/impl/DirectoryMap.java
@@ -1,1120 +1,1120 @@
-/*******************************************************************************
- * 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.
- *
- * This class is an implmentation to Map(Variant, Variant) -Accessor.
- *
- * Filenames have the following encoding:
- * S.dbb String types, if string doesn't have the following
- * control characters " : < > | ? * \ / [0..31]
- * I.dbb Integer types
- * L.dbb Long types
- * H.dbb All other cases the value as binary
- *
- * 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.
- *
- * DirectoryMap must be closed with #close();
- *
- * @author Toni Kalajainen
- */
-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 deleteList = dir.files();
- List failList = new ArrayList();
- boolean hasListeners = listeners!=null;
- List keys = hasListeners ? new ArrayList() : 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 dstKeys = new TreeSet(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 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 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 createKeys() throws RuntimeBindingException {
- List files = dir.files();
- TreeSet keys = new TreeSet(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 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 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 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 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 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 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 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 keys = createKeys();
- int count = keys.size();
- Object[] result = new Object[ count ];
-
- Iterator iter = keys.iterator();
- for (int i=0; i from) throws AccessorException {
- //boolean created = false;
- for (Entry 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 writeFiles = new HashSet();
- List oldFiles = dir.files();
-
- //HashSet modifiedFiles = new HashSet();
- HashSet addedFiles = new HashSet(writeFiles);
- addedFiles.removeAll(oldFiles);
-
- // Write
- for (int i=0; i removedFiles = new HashSet(oldFiles);
- removedFiles.removeAll(writeFiles);
-
- // Remove old files
- files.expunge();
- if (!removedFiles.isEmpty()) {
- List failList = new ArrayList();
- 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 cs, LinkedList rollback) throws AccessorException {
- try {
- boolean makeRollback = rollback != null;
- ArrayList single = new ArrayList();
- 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 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 events) {
- for (Event e : events)
- e.reference = ChildReference.concatenate(le.path, e.reference);
- le.emitEvents(events);
- }
-
-
-}
-
+/*******************************************************************************
+ * 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.
+ *
+ * This class is an implmentation to Map(Variant, Variant) -Accessor.
+ *
+ * Filenames have the following encoding:
+ * S.dbb String types, if string doesn't have the following
+ * control characters " : < > | ? * \ / [0..31]
+ * I.dbb Integer types
+ * L.dbb Long types
+ * H.dbb All other cases the value as binary
+ *
+ * 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.
+ *
+ * DirectoryMap must be closed with #close();
+ *
+ * @author Toni Kalajainen
+ */
+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 deleteList = dir.files();
+ List failList = new ArrayList();
+ boolean hasListeners = listeners!=null;
+ List keys = hasListeners ? new ArrayList() : 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 dstKeys = new TreeSet(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 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 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 createKeys() throws RuntimeBindingException {
+ List files = dir.files();
+ TreeSet keys = new TreeSet(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 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 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 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 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 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 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 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 keys = createKeys();
+ int count = keys.size();
+ Object[] result = new Object[ count ];
+
+ Iterator iter = keys.iterator();
+ for (int i=0; i from) throws AccessorException {
+ //boolean created = false;
+ for (Entry 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 writeFiles = new HashSet();
+ List oldFiles = dir.files();
+
+ //HashSet modifiedFiles = new HashSet();
+ HashSet addedFiles = new HashSet(writeFiles);
+ addedFiles.removeAll(oldFiles);
+
+ // Write
+ for (int i=0; i removedFiles = new HashSet(oldFiles);
+ removedFiles.removeAll(writeFiles);
+
+ // Remove old files
+ files.expunge();
+ if (!removedFiles.isEmpty()) {
+ List failList = new ArrayList();
+ 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 cs, LinkedList rollback) throws AccessorException {
+ try {
+ boolean makeRollback = rollback != null;
+ ArrayList single = new ArrayList();
+ 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 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 events) {
+ for (Event e : events)
+ e.reference = ChildReference.concatenate(le.path, e.reference);
+ le.emitEvents(events);
+ }
+
+
+}
+