1 /*******************************************************************************
3 * Copyright (c) 2010- Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.databoard.accessor.impl;
15 import java.io.FileFilter;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.HashSet;
19 import java.util.Iterator;
20 import java.util.LinkedList;
21 import java.util.List;
23 import java.util.Map.Entry;
25 import java.util.TreeSet;
26 import java.util.concurrent.Executor;
28 import org.simantics.databoard.Bindings;
29 import org.simantics.databoard.Datatypes;
30 import org.simantics.databoard.accessor.Accessor;
31 import org.simantics.databoard.accessor.CloseableAccessor;
32 import org.simantics.databoard.accessor.MapAccessor;
33 import org.simantics.databoard.accessor.VariantAccessor;
34 import org.simantics.databoard.accessor.error.AccessorConstructionException;
35 import org.simantics.databoard.accessor.error.AccessorException;
36 import org.simantics.databoard.accessor.error.ReferenceException;
37 import org.simantics.databoard.accessor.event.Event;
38 import org.simantics.databoard.accessor.event.MapEntryAdded;
39 import org.simantics.databoard.accessor.event.MapEntryRemoved;
40 import org.simantics.databoard.accessor.event.ValueAssigned;
41 import org.simantics.databoard.accessor.file.FileLibrary;
42 import org.simantics.databoard.accessor.file.FileVariantAccessor;
43 import org.simantics.databoard.accessor.impl.DirectoryWatch.DirectoryEvent;
44 import org.simantics.databoard.accessor.impl.DirectoryWatch.DirectoryListener;
45 import org.simantics.databoard.accessor.interestset.InterestSet;
46 import org.simantics.databoard.accessor.interestset.MapInterestSet;
47 import org.simantics.databoard.accessor.reference.ChildReference;
48 import org.simantics.databoard.accessor.reference.KeyReference;
49 import org.simantics.databoard.accessor.reference.LabelReference;
50 import org.simantics.databoard.adapter.AdaptException;
51 import org.simantics.databoard.adapter.Adapter;
52 import org.simantics.databoard.adapter.AdapterConstructionException;
53 import org.simantics.databoard.binding.ArrayBinding;
54 import org.simantics.databoard.binding.Binding;
55 import org.simantics.databoard.binding.MapBinding;
56 import org.simantics.databoard.binding.VariantBinding;
57 import org.simantics.databoard.binding.error.BindingConstructionException;
58 import org.simantics.databoard.binding.error.BindingException;
59 import org.simantics.databoard.binding.error.RuntimeBindingException;
60 import org.simantics.databoard.binding.mutable.MutableVariant;
61 import org.simantics.databoard.type.Datatype;
62 import org.simantics.databoard.type.MapType;
65 * DirectoryMap is a file backed map implementation where keys are filenames
66 * and values are corresponding files.
68 * This class is an implmentation to Map(Variant, Variant) -Accessor.
70 * Filenames have the following encoding:
71 * S<string>.dbb String types, if string doesn't have the following
72 * control characters " : < > | ? * \ / [0..31]
73 * I<integer>.dbb Integer types
74 * L<long>.dbb Long types
75 * H<hex>.dbb All other cases the value as binary
77 * File accessor is created if an entry opened as a sub-accessor.
78 * The file accessor is closed when all sub-accessors are released.
79 * The implementation is based on proxy instances and a reference queue.
80 * Once the queue is empty, file accessor is closed.
82 * DirectoryMap must be closed with #close();
84 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
86 public class DirectoryMap implements MapAccessor, CloseableAccessor {
89 final static Binding KEY_BINDING = Bindings.STR_VARIANT;
91 /** Cache of sub-accessors */
94 /** Monitors directory for file changes */
101 ListenerEntry listeners = null;
103 /** Parent, optional */
106 /** Accessor params */
107 AccessorParams params;
109 DirectoryListener dirListener = new DirectoryListener() {
111 public void onWatchEvent(DirectoryEvent e) {
116 public DirectoryMap(File directory) {
117 this(directory, null, AccessorParams.DEFAULT);
120 public DirectoryMap(File directory, Accessor parent) {
121 this(directory, parent, AccessorParams.DEFAULT);
124 public DirectoryMap(File directory, Accessor parent, AccessorParams params) {
125 this.parent = parent;
126 this.path = directory;
127 this.params = params;
129 // Filters .dbb files
130 FileFilter filter = new FileFilter() {
131 public boolean accept(File pathname) {
132 String filename = pathname.getName();
133 if (filename.length()==0) return false;
134 char c = filename.charAt(0);
135 if (c!='S' && c!='I' && c!='L' && c!='B') return false;
136 if (filename.endsWith(".dbb")) return true;
137 return filename.toLowerCase().endsWith(".dbb");
140 dir = new DirectoryWatch(path, filter);
142 dir.addListener( dirListener );
144 files = new FileLibrary();
147 public void close() {
148 dir.removeListener( dirListener );
153 static MapType type = new MapType(Datatypes.VARIANT, Datatypes.VARIANT);
154 public MapType type() {
158 private String fileToKey(File f) {
159 String filename = f.getName();
160 String keyStr = filename.substring(0, filename.length()-4);
164 private File keyToFile(String keyStr) {
165 return new File(path, keyStr + ".dbb" );
169 public void clear() throws AccessorException {
170 //List<File> deleteList = dir.files();
171 List<File> failList = new ArrayList<File>();
172 boolean hasListeners = listeners!=null;
173 List<String> keys = hasListeners ? new ArrayList<String>() : null;
175 // Close all file handles
179 for (File f : dir.files()) {
180 if (!files.deleteFile(f)) {
184 String keyStr = fileToKey(f);
193 ListenerEntry le = listeners;
195 MapInterestSet is = le.getInterestSet();
196 for (Object key : keys) {
197 MutableVariant var = new MutableVariant(KEY_BINDING, key);
198 if (is.inNotificationsOf(var)) {
199 MapEntryRemoved e = new MapEntryRemoved(var);
206 // Some files failed to delete
207 if (!failList.isEmpty()) {
208 StringBuilder sb = new StringBuilder();
209 sb.append("Failed to delete");
210 for (File f : failList) {
212 sb.append(f.toString());
215 throw new AccessorException(sb.toString());
222 public String toString() {
223 return dir.toString();
226 public Object getValue(Binding binding) throws AccessorException {
227 MapBinding mb = (MapBinding) binding;
228 if (mb.getKeyBinding() instanceof VariantBinding==false || mb.getValueBinding() instanceof VariantBinding==false)
229 throw new AccessorException("Map(Variant, Variant) Expected");
230 // Get all files as a single map
232 Object result = binding.createDefault();
233 for (File f : dir.files()) {
235 String keyStr = fileToKey(f);
236 Object key = params.adapterScheme.adapt(keyStr, KEY_BINDING, mb.getKeyBinding());
239 VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);
240 Object value = va.getValue(mb.getValueBinding());
242 mb.put(result, key, value);
245 } catch (BindingException e) {
246 throw new AccessorException(e);
247 } catch (AccessorConstructionException e) {
248 throw new AccessorException(e);
249 } catch (AdaptException e) {
250 throw new AccessorException(e);
255 public void getValue(Binding dstBinding, Object dst) throws AccessorException {
256 MapBinding db = (MapBinding) dstBinding;
257 Binding dkb = db.getKeyBinding();
258 Binding dvb = db.getValueBinding();
259 if (dkb instanceof VariantBinding==false || dvb instanceof VariantBinding==false)
260 throw new AccessorException("Map(Variant, Variant) Expected");
261 // Get all files as a single map
263 TreeSet<Object> dstKeys = new TreeSet<Object>(dkb);
264 db.getKeys(dst, dstKeys);
266 for (File f : dir.files()) {
268 String keyStr = fileToKey(f);
269 Object key = params.adapterScheme.adapt(keyStr, KEY_BINDING, dkb);
271 Object v = db.containsKey(dst, key) ? db.get(dst, key) : dvb.createDefault();
272 VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);
279 for (Object key : dstKeys)
281 } catch (BindingException e) {
282 throw new AccessorException(e);
283 } catch (AccessorConstructionException e) {
284 throw new AccessorException(e);
285 } catch (AdaptException e) {
286 throw new AccessorException(e);
292 public boolean getValue(ChildReference path, Binding binding, Object obj) throws AccessorException {
294 Accessor a = getComponent(path);
295 a.getValue(binding, obj);
297 } catch (ReferenceException re) {
299 } catch (AccessorConstructionException e) {
300 throw new AccessorException(e);
304 public Object getValue(ChildReference path, Binding binding) throws AccessorException {
306 Accessor a = getComponent(path);
307 return a.getValue(binding);
308 } catch (ReferenceException re) {
310 } catch (AccessorConstructionException e) {
311 throw new AccessorException(e);
317 public boolean containsKey(Binding keyBinding, Object key)
318 throws AccessorException {
320 String key_ = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
321 File file = keyToFile(key_);
322 return dir.files().contains(file);
323 } catch (AdaptException e) {
324 throw new AccessorException(e);
329 public boolean containsValue(Binding valueBinding, Object value)
330 throws AccessorException {
332 for (File f : dir.files()) {
333 String key = fileToKey(f);
334 VariantAccessor va = getValueAccessor(KEY_BINDING, key);
335 Object v = va.getValue(valueBinding);
336 boolean match = valueBinding.equals(v, value);
337 if ( match ) return true;
340 } catch (AccessorConstructionException e) {
341 throw new AccessorException(e);
346 public Object get(Binding keyBinding, Object key, Binding valueBinding)
347 throws AccessorException {
349 VariantAccessor va = getValueAccessor(keyBinding, key);
350 return va.getValue(valueBinding);
351 } catch (AccessorConstructionException e) {
352 throw new AccessorException(e);
357 * Get the value as a variant
362 * @throws AccessorException
364 public MutableVariant getAsVariant(Binding keyBinding, Object key)
365 throws AccessorException {
367 VariantAccessor va = getValueAccessor(keyBinding, key);
368 Datatype type = va.getContentType();
369 Binding binding = params.bindingScheme.getBinding(type);
370 Object value = va.getContentValue(binding);
371 MutableVariant result = new MutableVariant(binding, value);
373 } catch (AccessorConstructionException e) {
374 throw new AccessorException(e);
375 } catch (BindingConstructionException e) {
376 throw new AccessorException(e);
381 public void getAll(Binding keyBinding, Binding valueBinding,
382 Map<Object, Object> to) throws AccessorException {
384 for (File f : dir.files()) {
386 String keyStr = fileToKey(f);
387 Object key = params.adapterScheme.adapt(keyStr, KEY_BINDING, keyBinding);
390 VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);
391 Object value = va.getValue(valueBinding);
395 } catch (AdaptException e) {
396 throw new AccessorException(e);
397 } catch (AccessorConstructionException e) {
398 throw new AccessorException(e);
403 public void getAll(Binding keyBinding, Binding valueBinding, Object[] keys,
404 Object[] values) throws AccessorException {
406 Set<String> fileKeys = createKeys();
409 for (String keyStr : fileKeys) {
411 VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);
412 Object value = va.getValue(valueBinding);
414 Object key2 = params.adapterScheme.adapt(keyStr, KEY_BINDING, keyBinding);
419 } catch (AdaptException e) {
420 throw new AccessorException(e);
421 } catch (AccessorConstructionException e) {
422 throw new AccessorException(e);
428 public int count(Binding keyBinding, Object from,
429 boolean fromInclusive, Object end, boolean endInclusive)
430 throws AccessorException {
431 throw new AccessorException("Not implemented");
435 public int getEntries(Binding keyBinding, Object from,
436 boolean fromInclusive, Object end, boolean endInclusive,
437 ArrayBinding keyArrayBinding, Object dstKeys,
438 ArrayBinding valueArrayBinding, Object dstValues, int limit)
439 throws AccessorException {
440 throw new AccessorException("Not implemented");
444 TreeSet<String> createKeys() throws RuntimeBindingException {
445 List<File> files = dir.files();
446 TreeSet<String> keys = new TreeSet<String>(KEY_BINDING);
448 for (File f : files) {
449 String filename = f.getName();
450 String str = filename.substring(0, filename.length()-4);
458 public Object getCeilingKey(Binding keyBinding, Object key)
459 throws AccessorException {
461 TreeSet<String> keys = createKeys();
462 String k = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
463 if (keys.contains(k)) return key;
464 Object res = keys.ceiling(k);
465 if (res==null) return null;
466 return params.adapterScheme.adapt(res, KEY_BINDING, keyBinding);
467 } catch (RuntimeBindingException e) {
468 throw new AccessorException(e);
469 } catch (AdaptException e) {
470 throw new AccessorException(e);
475 public Object getFirstKey(Binding keyBinding) throws AccessorException {
476 List<File> files = dir.files();
477 String firstKey = null;
479 for (File f : files) {
480 String filename = f.getName();
481 String str = filename.substring(0, filename.length()-4);
482 if (firstKey == null) {
485 if (KEY_BINDING.compare(str, firstKey)<0) firstKey = str;
488 if (firstKey==null) return null;
491 return params.adapterScheme.adapt(firstKey, KEY_BINDING, keyBinding);
492 } catch (AdaptException e) {
493 throw new AccessorException(e);
498 public Object getFloorKey(Binding keyBinding, Object key)
499 throws AccessorException {
501 TreeSet<String> keys = createKeys();
502 String k = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
503 Object res = keys.floor(k);
504 if (res==null) return null;
505 return params.adapterScheme.adapt(res, KEY_BINDING, keyBinding);
506 } catch (RuntimeBindingException e) {
507 throw new AccessorException(e);
508 } catch (AdaptException e) {
509 throw new AccessorException(e);
514 public Object getHigherKey(Binding keyBinding, Object key)
515 throws AccessorException {
517 TreeSet<String> keys = createKeys();
518 String k = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
519 Object res = keys.higher(k);
520 if (res==null) return null;
521 return params.adapterScheme.adapt(res, KEY_BINDING, keyBinding);
522 } catch (RuntimeBindingException e) {
523 throw new AccessorException(e);
524 } catch (AdaptException e) {
525 throw new AccessorException(e);
530 public Object[] getKeys(Binding keyBinding) throws AccessorException {
531 TreeSet<String> keys = createKeys();
532 Object[] result = new Object[keys.size()];
533 if (keys.isEmpty()) return result;
535 Adapter a = params.adapterScheme.getAdapter(KEY_BINDING, keyBinding, true, false);
537 for (String key : keys) {
538 result[index++] = a.adapt( key );
540 } catch (AdaptException e) {
541 throw new AccessorException(e);
542 } catch (AdapterConstructionException e) {
543 throw new AccessorException(e);
550 public Object getLastKey(Binding keyBinding) throws AccessorException {
551 List<File> files = dir.files();
552 String lastKey = null;
554 for (File f : files) {
555 String filename = f.getName();
556 String str = filename.substring(0, filename.length()-4);
557 if (lastKey == null) {
560 if (KEY_BINDING.compare(str, lastKey)>0) lastKey = str;
563 if (lastKey==null) return null;
566 return params.adapterScheme.adapt(lastKey, KEY_BINDING, keyBinding);
567 } catch (AdaptException e) {
568 throw new AccessorException(e);
573 public Object getLowerKey(Binding keyBinding, Object key)
574 throws AccessorException {
576 TreeSet<String> keys = createKeys();
577 String k = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
578 Object res = keys.lower(k);
579 if (res==null) return null;
580 return params.adapterScheme.adapt(res, KEY_BINDING, keyBinding);
581 } catch (RuntimeBindingException e) {
582 throw new AccessorException(e);
583 } catch (AdaptException e) {
584 throw new AccessorException(e);
588 public FileVariantAccessor getExistingAccessor(Binding keyBinding,
589 Object key) throws AccessorConstructionException {
591 String key_ = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
592 File file = new File(path, key_ + ".dbb" );
593 return files.getExistingFile(file);
594 } catch (AdaptException e) {
595 throw new AccessorConstructionException(e);
599 @SuppressWarnings("unchecked")
601 public FileVariantAccessor getValueAccessor(Binding keyBinding, Object key) throws AccessorConstructionException {
603 String keyStr = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
604 File file = keyToFile(keyStr);
605 FileVariantAccessor sa = files.getExistingFile(file);
606 if (sa!=null) return sa;
608 // Create new accessor
609 sa = files.getFile(file);
611 // Add component interest sets
612 ListenerEntry le = listeners;
614 MutableVariant kv = new MutableVariant(keyBinding, key);
616 MapInterestSet is = le.getInterestSet();
618 // Generic element interest
619 InterestSet gis = is.getComponentInterest();
622 ChildReference childPath = ChildReference.concatenate(le.path, new KeyReference(kv) );
623 sa.addListener(le.listener, gis, childPath, le.executor);
624 } catch (AccessorException e) {
625 throw new AccessorConstructionException(e);
629 // Specific element interest
630 InterestSet cis = is.getComponentInterest(kv);
633 ChildReference childPath = ChildReference.concatenate(le.path, new KeyReference(kv) );
634 sa.addListener(le.listener, cis, childPath, le.executor);
635 } catch (AccessorException e) {
636 throw new AccessorConstructionException(e);
646 } catch (AdaptException e) {
647 throw new AccessorConstructionException(e);
652 public Object[] getValues(Binding valueBinding) throws AccessorException {
654 Set<String> keys = createKeys();
655 int count = keys.size();
656 Object[] result = new Object[ count ];
658 Iterator<String> iter = keys.iterator();
659 for (int i=0; i<count; i++) {
660 String keyStr = iter.next();
662 VariantAccessor va = getValueAccessor(KEY_BINDING, keyStr);
663 Object value = va.getValue(valueBinding);
667 } catch (AccessorConstructionException e) {
668 throw new AccessorException(e);
676 * @param valueBinding
678 * @return true if new file was created
679 * @throws AccessorException
681 private boolean putLocal(Binding keyBinding, Object key, Binding valueBinding,
682 Object value) throws AccessorException {
685 String _key = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
686 File file = new File(path, _key+".dbb");
687 boolean created = !dir.files().contains(file);
688 FileVariantAccessor va = files.createFile( file );
689 va.setValue(valueBinding, value);
696 MutableVariant kv = new MutableVariant(KEY_BINDING, _key);
699 if (listeners!=null) {
700 ListenerEntry le = listeners;
702 MapInterestSet is = le.getInterestSet();
703 if (is.inNotificationsOf(kv)) {
705 MutableVariant vv = null;
706 if (is.inValuesOf(kv)) vv = new MutableVariant(valueBinding, valueBinding.isImmutable() ? value : valueBinding.clone(value));
709 // Notify about new assignment to old value
710 ValueAssigned e = new ValueAssigned( new KeyReference(kv), vv);
713 // Notify about new entry
714 MapEntryAdded e = new MapEntryAdded(kv, vv);
723 } catch (AdaptException e) {
724 throw new AccessorException(e);
725 } catch (AccessorConstructionException e) {
726 throw new AccessorException(e);
731 public void put(Binding keyBinding, Object key, Binding valueBinding,
732 Object value) throws AccessorException {
733 /*boolean created =*/ putLocal(keyBinding, key, valueBinding, value);
737 public void putAll(Binding keyBinding, Binding valueBinding,
738 Map<Object, Object> from) throws AccessorException {
739 //boolean created = false;
740 for (Entry<Object, Object> e : from.entrySet()) {
741 Object key = e.getKey();
742 Object value = e.getValue();
743 /*created |=*/ putLocal(keyBinding, key, valueBinding, value);
748 public void putAll(Binding keyBinding, Binding valueBinding, Object[] keys,
749 Object[] values) throws AccessorException {
750 //boolean created = false;
751 if (keys.length!=values.length)
752 throw new AccessorException("Array lengths mismatch");
753 for (int i=0; i<keys.length; i++) {
754 Object key = keys[i];
755 Object value = values[i];
756 /*created |=*/ putLocal(keyBinding, key, valueBinding, value);
761 public void remove(Binding keyBinding, Object key) throws AccessorException {
764 String key_ = (String) params.adapterScheme.adapt(key, keyBinding, KEY_BINDING);
765 File file = new File(path, key_ + ".dbb" );
766 if (!dir.files().contains(file)) return;
767 // throw new AccessorException("File "+file+" does not exist");
771 boolean deleteOk = files.deleteFile(file);
774 throw new AccessorException("Failed to delete "+file);
780 if (listeners!=null) {
781 MutableVariant var = new MutableVariant(KEY_BINDING, key_);
782 ListenerEntry le = listeners;
784 MapInterestSet is = le.getInterestSet();
785 if (is.inNotificationsOf(var)) {
786 MapEntryRemoved e = new MapEntryRemoved(var);
793 } catch (AdaptException e) {
794 throw new AccessorException(e);
799 public boolean setValue(ChildReference path, Binding binding, Object obj) throws AccessorException {
801 Accessor a = getComponent(path);
802 a.setValue(binding, obj);
804 } catch (ReferenceException re) {
806 } catch (AccessorConstructionException e) {
807 throw new AccessorException(e);
812 public void setValue(Binding mapBinding, Object newMap)
813 throws AccessorException {
815 MapBinding mb = (MapBinding) mapBinding;
816 Binding valueBinding = mb.getValueBinding();
817 int size = mb.size(newMap);
818 Object keys[] = new Object[size];
819 Object values[] = new Object[size];
820 mb.getAll(newMap, keys, values);
822 Adapter keyAdapter = params.adapterScheme.getAdapter(mb.getKeyBinding(), KEY_BINDING, true, false);
823 HashSet<File> writeFiles = new HashSet<File>();
824 List<File> oldFiles = dir.files();
826 //HashSet<File> modifiedFiles = new HashSet<File>();
827 HashSet<File> addedFiles = new HashSet<File>(writeFiles);
828 addedFiles.removeAll(oldFiles);
831 for (int i=0; i<keys.length; i++) {
832 Object key = keys[i];
833 Object value = values[i];
834 String _key = (String) keyAdapter.adapt(key);
835 String filename = _key + ".dbb";
836 File file = new File(path, filename);
837 writeFiles.add(file);
839 boolean existed = oldFiles.contains(file);
840 FileVariantAccessor va = files.createFile(file);
842 va.setValue(mb.getValueBinding(), value);
846 MutableVariant kv = new MutableVariant(KEY_BINDING, _key);
849 if (listeners!=null) {
850 ListenerEntry le = listeners;
852 MapInterestSet is = le.getInterestSet();
853 if (is.inNotificationsOf(kv)) {
855 MutableVariant vv = null;
856 if (is.inValuesOf(kv)) vv = new MutableVariant(valueBinding, valueBinding.isImmutable() ? value : valueBinding.clone(value));
859 // Notify about new assignment to old value
860 ValueAssigned e = new ValueAssigned( new KeyReference(kv), vv);
863 // Notify about new entry
864 MapEntryAdded e = new MapEntryAdded(kv, vv);
874 HashSet<File> removedFiles = new HashSet<File>(oldFiles);
875 removedFiles.removeAll(writeFiles);
879 if (!removedFiles.isEmpty()) {
880 List<File> failList = new ArrayList<File>();
881 for (File f : removedFiles) {
883 String filename = f.getName();
884 String keyStr = filename.substring(0, filename.length()-4);
886 boolean deleted = files.deleteFile(f);
892 if (listeners!=null) {
893 MutableVariant var = new MutableVariant(KEY_BINDING, keyStr);
894 ListenerEntry le = listeners;
896 MapInterestSet is = le.getInterestSet();
897 if (is.inNotificationsOf(var)) {
898 MapEntryRemoved e = new MapEntryRemoved(var);
906 if (!failList.isEmpty()) {
907 StringBuilder sb = new StringBuilder();
908 sb.append("Failed to delete");
909 for (File ff : failList) {
911 sb.append(ff.toString());
913 throw new AccessorException(sb.toString());
919 } catch (BindingException e) {
920 throw new AccessorException(e);
921 } catch (AdaptException e) {
922 throw new AccessorException(e);
923 } catch (AdapterConstructionException e) {
924 throw new AccessorException(e);
925 } catch (AccessorConstructionException e) {
926 throw new AccessorException(e);
931 public int size() throws AccessorException {
933 return dir.files().size();
937 public void addListener(Listener listener, InterestSet interestSet, ChildReference path, Executor executor) throws AccessorException {
938 listeners = ListenerEntry.link(listeners, listener, interestSet, path, executor);
939 MapInterestSet is = (MapInterestSet) interestSet;
942 for (File f : dir.files()) {
943 String filename = f.getName();
944 String keyStr = filename.substring(0, filename.length()-4);
946 Accessor sa = getExistingAccessor(KEY_BINDING, keyStr);
947 if (sa==null) continue;
949 MutableVariant key = new MutableVariant(KEY_BINDING, keyStr);
950 InterestSet cis = is.getComponentInterest();
952 ChildReference childPath = ChildReference.concatenate( path, new KeyReference(key) );
953 sa.addListener(listener, cis, childPath, executor);
955 cis = is.getComponentInterest( key );
957 ChildReference childPath = ChildReference.concatenate( path, new KeyReference(key) );
958 sa.addListener(listener, cis, childPath, executor);
961 } catch (AccessorConstructionException e) {
962 throw new AccessorException(e);
968 public void apply(List<Event> cs, LinkedList<Event> rollback) throws AccessorException {
970 boolean makeRollback = rollback != null;
971 ArrayList<Event> single = new ArrayList<Event>();
973 if (e.reference==null) {
974 Event rbe = applyLocal(e, makeRollback);
976 rbe.reference = e.reference;
977 rollback.addFirst( rbe );
980 Accessor sa = getComponent(e.reference);
983 Event noRefEvent = e.clone(null);
984 single.add(noRefEvent);
985 sa.apply(single, rollback);
988 } catch (AccessorConstructionException ae) {
989 throw new AccessorException(ae);
993 Event applyLocal(Event e, boolean makeRollback) throws AccessorException {
994 Event rollback = null;
996 if (e instanceof ValueAssigned) {
997 ValueAssigned va = (ValueAssigned) e;
999 Binding binding = params.bindingScheme.getBinding(type());
1000 rollback = new ValueAssigned(binding, getValue(binding));
1002 setValue(va.newValue.getBinding(), va.newValue.getValue());
1004 } else if (e instanceof MapEntryAdded) {
1005 MapEntryAdded ea = (MapEntryAdded) e;
1006 if (ea.key==null) throw new AccessorException("Cannot apply entry added event because key is missing");
1007 if (ea.value==null) throw new AccessorException("Cannot apply entry added event because value is missing");
1008 boolean hadValue = containsKey(ea.key.getBinding(), ea.key.getValue());
1009 if (hadValue) throw new AccessorException("Could not add entry to key that already existed");
1012 rollback = new MapEntryRemoved( ea.key );
1015 put(ea.key.getBinding(), ea.key.getValue(), ea.value.getBinding(), ea.value.getValue());
1017 } else if (e instanceof MapEntryRemoved) {
1018 MapEntryRemoved er = (MapEntryRemoved) e;
1021 boolean hadValue = containsKey(er.key.getBinding(), er.key.getValue());
1024 MutableVariant oldKey = er.key;
1025 MutableVariant oldValue = getAsVariant(er.key.getBinding(), er.key.getValue());
1026 rollback = new MapEntryAdded(oldKey, oldValue);
1028 rollback = new MapEntryRemoved( er.key.clone() );
1032 remove( er.key.getBinding(), er.key.getValue() );
1034 } else throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Map Type");
1037 } catch (BindingConstructionException e2) {
1038 throw new AccessorException( e2 );
1043 @SuppressWarnings("unchecked")
1045 public <T extends Accessor> T getComponent(ChildReference reference)
1046 throws AccessorConstructionException {
1047 if (reference==null) return (T) this;
1048 if (reference instanceof LabelReference) {
1050 LabelReference lr = (LabelReference) reference;
1052 MutableVariant variant = (MutableVariant) params.adapterScheme.adapt(lr.label, Bindings.STRING, Bindings.MUTABLE_VARIANT);
1053 Object value = variant.getValue(KEY_BINDING);
1054 Accessor result = (T) getValueAccessor(KEY_BINDING, value);
1056 if (reference.getChildReference() != null)
1057 result = result.getComponent(reference.getChildReference());
1059 } catch (AdaptException e) {
1060 throw new ReferenceException(e);
1062 } else if (reference instanceof KeyReference) {
1064 KeyReference ref = (KeyReference) reference;
1065 String keyStr = (String) params.adapterScheme.adapt(ref.key.getValue(), ref.key.getBinding(), KEY_BINDING);
1066 File f = keyToFile(keyStr);
1067 if (!dir.files().contains(f))
1068 throw new AccessorConstructionException("Invalid reference "+ref.key);
1070 Accessor result = getValueAccessor(KEY_BINDING, keyStr);
1071 if (reference.getChildReference() != null)
1072 result = result.getComponent(reference.getChildReference());
1074 } catch (AdaptException e) {
1075 throw new ReferenceException(e);
1078 throw new ReferenceException(reference.getClass().getName()+" is not a reference of a map");
1082 public void removeListener(Listener listener) throws AccessorException {
1083 detachListener(listener);
1086 protected ListenerEntry detachListener(Listener listener) throws AccessorException {
1087 ListenerEntry e = listeners;
1088 ListenerEntry p = null;
1091 if (e.listener == listener) {
1092 // The match was the first entry of the linked list
1097 // Some other entry, unlink e
1107 protected void emitEvent(ListenerEntry le, Event e) {
1108 e.reference = ChildReference.concatenate(le.path, e.reference);
1112 protected void emitEvents(ListenerEntry le, Collection<Event> events) {
1113 for (Event e : events)
1114 e.reference = ChildReference.concatenate(le.path, e.reference);
1115 le.emitEvents(events);