1 /*******************************************************************************
2 * 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.binary;
14 import java.io.IOException;
15 import java.lang.ref.WeakReference;
16 import java.util.Collection;
17 import java.util.HashMap;
19 import java.util.Map.Entry;
20 import java.util.SortedMap;
21 import java.util.TreeMap;
23 import org.simantics.databoard.accessor.Accessor;
24 import org.simantics.databoard.accessor.ArrayAccessor;
25 import org.simantics.databoard.accessor.StreamAccessor;
26 import org.simantics.databoard.accessor.error.AccessorConstructionException;
27 import org.simantics.databoard.accessor.error.AccessorException;
28 import org.simantics.databoard.accessor.error.ReferenceException;
29 import org.simantics.databoard.accessor.event.ArrayElementAdded;
30 import org.simantics.databoard.accessor.event.ArrayElementRemoved;
31 import org.simantics.databoard.accessor.event.Event;
32 import org.simantics.databoard.accessor.event.ValueAssigned;
33 import org.simantics.databoard.accessor.file.FileArrayAccessor;
34 import org.simantics.databoard.accessor.impl.AccessorParams;
35 import org.simantics.databoard.accessor.impl.ListenerEntry;
36 import org.simantics.databoard.accessor.interestset.ArrayInterestSet;
37 import org.simantics.databoard.accessor.interestset.InterestSet;
38 import org.simantics.databoard.accessor.reference.ChildReference;
39 import org.simantics.databoard.accessor.reference.IndexReference;
40 import org.simantics.databoard.accessor.reference.LabelReference;
41 import org.simantics.databoard.adapter.AdaptException;
42 import org.simantics.databoard.binding.ArrayBinding;
43 import org.simantics.databoard.binding.Binding;
44 import org.simantics.databoard.binding.error.BindingException;
45 import org.simantics.databoard.binding.mutable.MutableVariant;
46 import org.simantics.databoard.serialization.Serializer;
47 import org.simantics.databoard.serialization.SerializerConstructionException;
48 import org.simantics.databoard.type.ArrayType;
49 import org.simantics.databoard.type.Datatype;
50 import org.simantics.databoard.util.binary.Blob;
51 import org.simantics.databoard.util.binary.RandomAccessBinary.ByteSide;
54 * Binary Array is accessor to a byte backed array of elements.
57 * Note, To increase the random access performance of the record, create sub-accessors of
60 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
62 public class BinaryArray extends BinaryObject implements ArrayAccessor, FileArrayAccessor, ArrayAccessor.CloseableArrayAccessor, StreamAccessor {
64 /** Accessors to children */
65 TreeMap<Integer, java.lang.ref.Reference<BinaryObject>> children = new TreeMap<Integer, java.lang.ref.Reference<BinaryObject>>();
71 public BinaryArray(BinaryObject parent, Blob blob, Datatype type, AccessorParams params) {
72 super(parent, blob, type, params);
73 ArrayType at = (ArrayType) type;
74 cb = params.bindingScheme.getBindingUnchecked(at.componentType);
75 cs = params.serializerScheme.getSerializerUnchecked( cb );
76 constantSize = cs.getConstantSize();
79 public ArrayType type() {
80 return (ArrayType) type;
86 * Get existing sub accessor
88 * @return sub-accessor or <code>null</code>
90 BinaryObject getExistingAccessor(int index)
92 java.lang.ref.Reference<BinaryObject> ref = children.get(index);
93 if (ref==null) return null;
94 BinaryObject res = (BinaryObject) ref.get();
95 // if (res==null) children.remove(index);
100 * Get start position of a field
104 * @throws AccessorException
106 long getStartPosition(int fieldIndex) throws AccessorException {
107 if (constantSize != null) return 4L + (fieldIndex * (long) (constantSize));
108 if (fieldIndex == 0) return 4;
110 // sa, saIndex = getFloorExistingAccessor
111 BinaryObject sa = null;
112 Map.Entry<Integer, java.lang.ref.Reference<BinaryObject>> sae = children.floorEntry(fieldIndex);
113 while (sae != null && sa == null) {
114 Integer key = sae.getKey();
115 sa = sae.getValue().get();
116 if (sa==null) children.remove(key); else break;
117 sae = children.floorEntry(key);
120 // Sub-accessor to the field exists
121 if (sa!=null && sae.getKey()==fieldIndex) return sa.b.getStartPositionInSourceBinary();
123 int si = sae==null ? 0 : sae.getKey();
124 long pos = sae==null ? 4L : sa.b.getStartPositionInSourceBinary();
126 for (int i=si; i<fieldIndex; i++) {
130 } catch (IOException ioe) {
131 throw new AccessorException( ioe );
135 long getLength(int index, long pos) throws AccessorException {
137 if (constantSize != null) return constantSize;
138 BinaryObject sa = getExistingAccessor(index);
139 if (sa != null) return sa.b.length();
140 BinaryObject ni = getExistingAccessor(index+1);
141 if (ni!=null) return ni.b.getStartPositionInSourceBinary() - pos;
144 return b.position() - pos;
145 } catch (IOException e) {
146 throw new AccessorException( e );
151 public void setNoflush(int index, Binding rcb, Object rcv)
152 throws AccessorException {
158 Serializer rcs = params.serializerScheme.getSerializer( rcb );
159 long pos = getStartPosition(index);
160 long oldSize = getLength(index, pos);
161 long newSize = rcs.getSize(rcv, null);
163 long diff = newSize - oldSize;
165 b.insertBytes( newSize - oldSize, ByteSide.Right );
167 b.removeBytes( oldSize - newSize, ByteSide.Right );
170 rcs.serialize(b, null, rcv);
174 BinaryObject sa = getExistingAccessor(index);
175 if (sa!=null && newSize != oldSize) {
176 sa.b.setPositionInSource(pos, newSize);
179 // Shift consecutive blobs
180 /* if (!children.isEmpty()) {
181 Integer lastKey = children.lastKey();
182 if (lastKey!=null && index+1 <= lastKey && diff!=0) {
183 SortedMap<Integer, Reference<BinaryObject>> pm = children.subMap(index+1, true, lastKey, true);
184 for (Entry<Integer, Reference<BinaryObject>> e : pm.entrySet()) {
185 BinaryObject sa_ = e.getValue().get();
186 if (sa_ == null) continue;
187 sa_.b.setPositionInSource( sa_.b.getStartPositionInSourceBinary() + diff, sa_.b.length());
193 ListenerEntry le = listeners;
195 ArrayInterestSet is = le.getInterestSet();
196 if (is.inNotificationsOf(index)) {
197 MutableVariant newValue = null;
198 if (is.inValues()) newValue = new MutableVariant(rcb, rcb.isImmutable() ? rcv : rcb.clone(rcv));
200 Event e = new ValueAssigned(new IndexReference(index), newValue);
206 } catch (IOException e) {
207 throw new AccessorException(e);
208 } catch (AdaptException e) {
209 throw new AccessorException(e);
210 } catch (SerializerConstructionException e) {
211 throw new AccessorException(e);
221 * @param arrayBinding
225 public void setValueNoflush(Binding arrayBinding, Object newArray)
226 throws AccessorException {
231 ArrayBinding rb = ((ArrayBinding)arrayBinding);
232 Binding rcb = rb.getComponentBinding();
233 Serializer rcs = params.serializerScheme.getSerializer( rcb );
235 int oldCount = b.readInt();
236 int newCount = rb.size(newArray);
237 b.setLength( params.serializerScheme.getSerializer( rb ).getSize(newArray) );
239 b.writeInt(newCount);
242 for (int index=0; index<newCount; index++) {
243 //long startPos = b.position();
244 Object obj = rb.get(newArray, index);
245 rcs.serialize(b, obj);
246 //long endPos = b.position();
247 //long len = endPos - startPos;
251 BinaryObject sa = getExistingAccessor(index);
253 sa.b.setPositionInSource(startPos, len);
258 for (int index=oldCount-1; index>=newCount; index--) {
259 BinaryObject sa = getExistingAccessor(index);
261 sa.invalidatedNotification();
262 children.remove(index);
267 ListenerEntry le = listeners;
269 ArrayInterestSet is = le.getInterestSet();
270 if (is.inNotificationsOf(index)) {
271 Event e = new ArrayElementRemoved(index);
278 // Notify new assignment
279 if (listeners!=null) {
280 for (int index=0; index<newCount; index++) {
281 Object cv = rb.get(newArray, index);
284 ListenerEntry le = listeners;
286 ArrayInterestSet is = le.getInterestSet();
287 if (is.inNotificationsOf(index)) {
288 MutableVariant vv = null;
289 if (is.inValues()) vv = new MutableVariant(rcb, cb.isImmutable() ? cv : rcb.clone(cv));
291 Event e = index<oldCount ? new ValueAssigned(new IndexReference(index), vv) : new ArrayElementAdded(index, vv);
299 } catch (BindingException e) {
300 throw new AccessorException(e);
301 } catch (IOException e) {
302 throw new AccessorException(e);
303 } catch (AdaptException e) {
304 throw new AccessorException(e);
305 } catch (SerializerConstructionException e) {
306 throw new AccessorException(e);
314 public void addNoflush(int index, Binding rcb, Object rcv) throws AccessorException {
318 Serializer rcs = params.serializerScheme.getSerializer( rcb );
321 int oldCount = b.readInt();
322 boolean lastEntry = index == oldCount;
323 if (index>oldCount) throw new AccessorException("Index out of range");
324 int newCount = oldCount +1;
326 b.writeInt(newCount);
328 long pos = getStartPosition(index);
329 int size = rcs.getSize(rcv);
331 b.insertBytes(size, ByteSide.Left);
332 rcs.serialize(b, null, rcv);
334 // Update child map keys
335 if (!lastEntry && !children.isEmpty()) {
336 Integer key = children.lastKey();
337 while (key != null && key >= index) {
338 java.lang.ref.Reference<BinaryObject> value = children.remove(key);
339 if (value.get()!=null) children.put(key+1, value);
340 key = children.lowerKey(key);
345 ListenerEntry le = listeners;
347 ArrayInterestSet is = le.getInterestSet();
348 if (is.inNotifications()) {
349 MutableVariant newValue = null;
350 if (is.inValues()) newValue = new MutableVariant(rcb, rcb.isImmutable() ? rcv : rcb.clone(rcv));
351 ArrayElementAdded e = new ArrayElementAdded(index, newValue);
355 // Update indices of interest sets
356 if (is.componentInterests!=null) {
357 Map<Integer, InterestSet> oldCis = is.componentInterests;
358 boolean needUpdates = false;
359 for (Integer i : oldCis.keySet()) {
360 needUpdates |= i>=index;
361 if (needUpdates) break;
365 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size());
366 for (Integer i : oldCis.keySet())
369 Integer newKey = i>=index ? i+1 : i;
370 InterestSet oldValue = oldCis.get(oldKey);
371 newCis.put(newKey, oldValue);
373 is.componentInterests = newCis;
377 // Add component interest listener
379 boolean hadSa = getExistingAccessor(index) != null;
381 // Add component interest listener
382 InterestSet cis = is.getComponentInterest();
384 Accessor sa = getAccessor(index);
386 cis = is.getComponentInterest(index);
388 Accessor sa = getAccessor(index);
395 } catch (IOException e) {
396 throw new AccessorException(e);
397 } catch (AdaptException e) {
398 throw new AccessorException(e);
399 } catch (SerializerConstructionException e) {
400 throw new AccessorException(e);
407 public void addNoflush(Binding binding, Object value)
408 throws AccessorException {
409 addNoflush(size(), binding, value);
413 public void addAllNoflush(Binding binding, Object[] values)
414 throws AccessorException {
415 addAllNoflush(size(), binding, values);
419 public void addAllNoflush(int index, Binding rcb, Object[] rcvs)
420 throws AccessorException {
421 if (index<0||index>size()) throw new AccessorException("Index out of bounds");
425 Serializer rcs = params.serializerScheme.getSerializer( rcb );
428 int oldCount = b.readInt();
429 int newCount = oldCount + rcvs.length;
430 if (index>oldCount) throw new AccessorException("Index out of range");
432 b.writeInt(newCount);
433 boolean lastEntry = index == oldCount;
436 for (int i=0; i<rcvs.length; i++)
437 size += rcs.getSize(rcvs[i]);
438 long pos = getStartPosition(index);
440 b.insertBytes(size, ByteSide.Right);
443 for (int i=0; i<rcvs.length; i++) {
444 Object rcv = rcvs[i];
445 rcs.serialize(b, rcv);
448 // Update child map keys
449 if (!lastEntry && !children.isEmpty()) {
450 Integer key = children.lastKey();
451 while (key!=null && key >= index) {
452 java.lang.ref.Reference<BinaryObject> value = children.remove(key);
453 if (value.get()!=null) children.put(key+rcvs.length, value);
454 key = children.lowerKey(key);
459 ListenerEntry le = listeners;
461 ArrayInterestSet is = le.getInterestSet();
462 if (is.inNotifications()) {
463 for (int i=0; i<rcvs.length; i++) {
464 MutableVariant newValue = null;
465 if (is.inValues()) newValue = new MutableVariant(rcb, rcb.isImmutable() ? rcvs[i] : cb.clone(rcvs[i]));
466 ArrayElementAdded e = new ArrayElementAdded(index, newValue);
471 // Update indices of interest sets
472 if (is.componentInterests!=null) {
473 Map<Integer, InterestSet> oldCis = is.componentInterests;
474 boolean needUpdates = false;
475 for (Integer i : oldCis.keySet()) {
476 needUpdates |= i>=index;
477 if (needUpdates) break;
481 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size());
482 for (Integer i : oldCis.keySet())
485 Integer newKey = i>=index ? i+rcvs.length : i;
486 InterestSet oldValue = oldCis.get(oldKey);
487 newCis.put(newKey, oldValue);
489 is.componentInterests = newCis;
492 // Add component interest listener
494 for (int i = index; i<index+rcvs.length; i++) {
495 boolean hadSa = getExistingAccessor(i)!=null;
498 InterestSet cis = is.getComponentInterest();
500 Accessor sa = getAccessor(i);
502 cis = is.getComponentInterest(index);
504 Accessor sa = getAccessor(i);
514 } catch (IOException e) {
515 throw new AccessorException(e);
516 } catch (AdaptException e) {
517 throw new AccessorException(e);
518 } catch (SerializerConstructionException e) {
519 throw new AccessorException(e);
525 void addRepeatNoflush(int index, Binding rcb, Object obj, int repeatCount) throws AccessorException {
526 if (index<0||index>size()) throw new AccessorException("Index out of bounds");
530 Serializer rcs = params.serializerScheme.getSerializer( rcb );
533 int oldCount = b.readInt();
534 int newCount = oldCount + repeatCount;
535 if (index>oldCount) throw new AccessorException("Index out of range");
537 b.writeInt(newCount);
538 boolean lastEntry = index == oldCount;
540 int size = rcs.getSize(obj) * repeatCount;
541 long pos = getStartPosition(index);
543 b.insertBytes(size, ByteSide.Right);
546 for (int i=0; i<repeatCount; i++) {
547 rcs.serialize(b, obj);
550 // Update child map keys
551 if (!lastEntry && !children.isEmpty()) {
552 Integer key = children.lastKey();
553 while (key!=null && key >= index) {
554 java.lang.ref.Reference<BinaryObject> value = children.remove(key);
555 if (value.get()!=null) children.put(key+repeatCount, value);
556 key = children.lowerKey(key);
561 ListenerEntry le = listeners;
563 ArrayInterestSet is = le.getInterestSet();
564 if (is.inNotifications()) {
565 for (int i=0; i<repeatCount; i++) {
566 MutableVariant newValue = null;
567 if (is.inValues()) newValue = new MutableVariant(rcb, obj/*rcb.isImmutable() ? obj : cb.clone(obj)*/);
568 ArrayElementAdded e = new ArrayElementAdded(index, newValue);
573 // Update indices of interest sets
574 if (is.componentInterests!=null) {
575 Map<Integer, InterestSet> oldCis = is.componentInterests;
576 boolean needUpdates = false;
577 for (Integer i : oldCis.keySet()) {
578 needUpdates |= i>=index;
579 if (needUpdates) break;
583 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size());
584 for (Integer i : oldCis.keySet())
587 Integer newKey = i>=index ? i+repeatCount : i;
588 InterestSet oldValue = oldCis.get(oldKey);
589 newCis.put(newKey, oldValue);
591 is.componentInterests = newCis;
599 } catch (IOException e) {
600 throw new AccessorException(e);
601 } catch (SerializerConstructionException e) {
602 throw new AccessorException(e);
609 public void removeNoflush(int index, int count) throws AccessorException {
614 boolean lastEntry = index == count;
616 int oldCount = b.readInt();
617 int newCount = oldCount - count;
618 if (index<0||index+count>oldCount) throw new AccessorException("Index out of bounds");
619 long pos = getStartPosition(index);
620 long lastPos = getStartPosition(index+count-1);
621 long lastLen = getLength(index, lastPos);
622 long end = lastPos + lastLen;
623 long len = end - pos;
625 b.writeInt(newCount);
627 b.removeBytes(len, ByteSide.Right);
630 SortedMap<Integer, java.lang.ref.Reference<BinaryObject>> sm = children.subMap(index, true, index+count, false);
631 for (Entry<Integer, java.lang.ref.Reference<BinaryObject>> e : sm.entrySet()) {
632 BinaryObject bo = e.getValue().get();
633 if (bo==null) continue;
634 bo.invalidatedNotification();
638 // Update the keys of consecutive children
639 if (!lastEntry && !children.isEmpty()) {
640 Integer lastKey = children.lastKey();
641 Integer key = children.higherKey(index);
642 while (key != null && key <= lastKey) {
643 java.lang.ref.Reference<BinaryObject> value = children.remove(key);
644 if (value.get()!=null) children.put(key-count, value);
645 key = children.higherKey(key);
650 ListenerEntry le = listeners;
652 ArrayInterestSet is = le.getInterestSet();
653 if (is.inNotifications()) {
654 ArrayElementRemoved e = new ArrayElementRemoved(index);
658 // Update indices of interest sets
659 if (is.componentInterests!=null) {
660 Map<Integer, InterestSet> oldCis = is.componentInterests;
661 boolean needUpdates = false;
662 for (Integer i : oldCis.keySet()) {
663 needUpdates |= i>=index;
664 if (needUpdates) break;
668 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size());
669 for (Integer i : oldCis.keySet())
672 Integer newKey = i>=index ? i-1 : i;
673 InterestSet oldValue = oldCis.get(oldKey);
674 newCis.put(newKey, oldValue);
676 is.componentInterests = newCis;
683 } catch (IOException e) {
684 throw new AccessorException( e );
691 public Object get(int index, Binding valueBinding) throws AccessorException {
695 long pos = getStartPosition(index);
697 Serializer s = params.serializerScheme.getSerializer(valueBinding);
698 return s.deserialize(b);
699 } catch (IOException e) {
700 throw new AccessorException(e);
701 } catch (SerializerConstructionException e) {
702 throw new AccessorException(e);
709 public void get(int index, Binding valueBinding, Object dst) throws AccessorException {
713 long pos = getStartPosition(index);
715 Serializer s = params.serializerScheme.getSerializer(valueBinding);
716 s.deserializeTo(b, dst);
717 } catch (IOException e) {
718 throw new AccessorException(e);
719 } catch (SerializerConstructionException e) {
720 throw new AccessorException(e);
727 @SuppressWarnings("unchecked")
729 public <T extends Accessor> T getAccessor(int index)
730 throws AccessorConstructionException {
735 int count = b.readInt();
736 if (index<0 || index>=count) throw new ReferenceException("Element index ("+index+") out of bounds ("+count+")");
738 // Get existing or create new
739 BinaryObject sa = getExistingAccessor(index);
741 long pos = getStartPosition(index);
742 long len = getLength(index, pos);
744 // Instantiate correct sub accessor.
745 sa = createSubAccessor(cb.type(), pos, len, params);
746 children.put(index, new WeakReference<BinaryObject>(sa) );
748 // Add component interest sets
749 ListenerEntry le = listeners;
751 ArrayInterestSet is = le.getInterestSet();
753 // Generic element interest
754 InterestSet gis = is.getComponentInterest();
757 ChildReference childPath = ChildReference.concatenate(le.path, new IndexReference(index) );
758 sa.addListener(le.listener, gis, childPath, le.executor);
759 } catch (AccessorException e) {
760 throw new AccessorConstructionException(e);
764 // Specific element interest
765 InterestSet cis = is.getComponentInterest(index);
768 ChildReference childPath = ChildReference.concatenate(le.path, new IndexReference(index) );
769 sa.addListener(le.listener, cis, childPath,le.executor);
770 } catch (AccessorException e) {
771 throw new AccessorConstructionException(e);
782 } catch (IOException e) {
783 throw new AccessorConstructionException(e);
784 } catch (AccessorException e) {
785 throw new AccessorConstructionException(e);
791 @SuppressWarnings("unchecked")
793 public <T extends Accessor> T getComponent(ChildReference reference)
794 throws AccessorConstructionException {
795 if (reference==null) return (T) this;
796 if (reference instanceof LabelReference) {
797 LabelReference lr = (LabelReference) reference;
799 Integer index = new Integer( lr.label );
800 Accessor result = getAccessor(index);
801 if (reference.getChildReference() != null)
802 result = result.getComponent(reference.getChildReference());
804 } catch ( NumberFormatException nfe ) {
805 throw new ReferenceException(nfe);
807 } else if (reference instanceof IndexReference) {
808 IndexReference ref = (IndexReference) reference;
809 int index = ref.getIndex();
810 Accessor result = getAccessor(index);
811 if (reference.getChildReference() != null)
812 result = result.getComponent(reference.getChildReference());
814 } throw new ReferenceException(reference.getClass().getName()+" is not a reference of an array");
818 public void getAll(Binding valueBinding, Object[] array)
819 throws AccessorException {
824 int size = b.readInt();
825 if (size > array.length) throw new AccessorException("Argument array too short");
826 Serializer s = params.serializerScheme.getSerializer(valueBinding);
827 for (int i=0; i<size; i++) {
828 array[i] = s.deserialize(b);
830 } catch (IOException e) {
831 throw new AccessorException( e );
832 } catch (SerializerConstructionException e) {
833 throw new AccessorException( e );
840 public void getAll(Binding valueBinding, Collection<Object> values)
841 throws AccessorException {
846 int size = b.readInt();
847 Serializer s = params.serializerScheme.getSerializer(valueBinding);
848 for (int i=0; i<size; i++) {
849 values.add( s.deserialize(b) );
851 } catch (IOException e) {
852 throw new AccessorException( e );
853 } catch (SerializerConstructionException e) {
854 throw new AccessorException( e );
861 public void setSizeNoflush(int newSize) throws AccessorException {
865 int oldSize = size();
868 if (newSize<oldSize) {
869 remove(newSize, oldSize-newSize);
872 // Add dummy instances
873 if (newSize>oldSize) {
874 Object dummy = cb.createDefault();
875 int count = newSize-oldSize;
876 addRepeatNoflush(oldSize, cb, dummy, count);
879 } catch (BindingException e) {
880 throw new AccessorException( e );
887 public void setSize(int newSize) throws AccessorException {
891 setSizeNoflush(newSize);
893 } catch (IOException e) {
894 throw new AccessorException( e );
901 public int size() throws AccessorException {
906 int size = b.readInt();
907 if (size<0) throw new AccessorException("Malformed file");
909 } catch (IOException e) {
910 throw new AccessorException( e );
917 Event applyLocal(Event e, boolean makeRollback) throws AccessorException {
918 Event rollback = null;
919 if (e instanceof ValueAssigned) {
920 ValueAssigned va = (ValueAssigned) e;
921 if (makeRollback) rollback = new ValueAssigned(cb, getValue(cb));
922 setValueNoflush(va.newValue.getBinding(), va.newValue.getValue());
924 if (e instanceof ArrayElementAdded) {
925 ArrayElementAdded aa = (ArrayElementAdded) e;
926 addNoflush(aa.index, aa.value.getBinding(), aa.value.getValue());
927 if (makeRollback) rollback = new ArrayElementRemoved(aa.index);
928 } else if (e instanceof ArrayElementRemoved) {
929 ArrayElementRemoved ar = (ArrayElementRemoved) e;
930 if (ar.index<0 || ar.index >=size()) throw new AccessorException("Array index out of bounds");
932 Object cv = get(ar.index, cb);
933 rollback = new ArrayElementAdded(ar.index, new MutableVariant(cb, cv));
935 removeNoflush(ar.index, 1);
937 throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Array");
946 public void add(Binding binding, Object value) throws AccessorException {
950 addNoflush(binding, value);
952 } catch (IOException e) {
953 throw new AccessorException( e );
960 public void add(int index, Binding binding, Object value)
961 throws AccessorException {
965 addNoflush(index, binding, value);
967 } catch (IOException e) {
968 throw new AccessorException( e );
975 public void addAll(Binding binding, Object[] values)
976 throws AccessorException {
980 addAllNoflush(binding, values);
982 } catch (IOException e) {
983 throw new AccessorException( e );
990 public void addAll(int index, Binding binding, Object[] values)
991 throws AccessorException {
995 addAllNoflush(index, binding, values);
997 } catch (IOException e) {
998 throw new AccessorException( e );
1005 public void remove(int index, int count) throws AccessorException {
1009 removeNoflush(index, count);
1011 } catch (IOException e) {
1012 throw new AccessorException( e );
1019 public void set(int index, Binding binding, Object value)
1020 throws AccessorException {
1024 setNoflush(index, binding, value);
1026 } catch (IOException e) {
1027 throw new AccessorException(e);