1 /*******************************************************************************
\r
2 * Copyright (c) 2010 Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.databoard.accessor.binary;
\r
14 import java.io.IOException;
\r
15 import java.lang.ref.WeakReference;
\r
16 import java.util.Collection;
\r
17 import java.util.HashMap;
\r
18 import java.util.Map;
\r
19 import java.util.Map.Entry;
\r
20 import java.util.SortedMap;
\r
21 import java.util.TreeMap;
\r
23 import org.simantics.databoard.Bindings;
\r
24 import org.simantics.databoard.accessor.Accessor;
\r
25 import org.simantics.databoard.accessor.ArrayAccessor;
\r
26 import org.simantics.databoard.accessor.CloseableAccessor;
\r
27 import org.simantics.databoard.accessor.StreamAccessor;
\r
28 import org.simantics.databoard.accessor.error.AccessorConstructionException;
\r
29 import org.simantics.databoard.accessor.error.AccessorException;
\r
30 import org.simantics.databoard.accessor.error.ReferenceException;
\r
31 import org.simantics.databoard.accessor.event.ArrayElementAdded;
\r
32 import org.simantics.databoard.accessor.event.ArrayElementRemoved;
\r
33 import org.simantics.databoard.accessor.event.Event;
\r
34 import org.simantics.databoard.accessor.event.ValueAssigned;
\r
35 import org.simantics.databoard.accessor.file.FileArrayAccessor;
\r
36 import org.simantics.databoard.accessor.impl.AccessorParams;
\r
37 import org.simantics.databoard.accessor.impl.ListenerEntry;
\r
38 import org.simantics.databoard.accessor.interestset.ArrayInterestSet;
\r
39 import org.simantics.databoard.accessor.interestset.InterestSet;
\r
40 import org.simantics.databoard.accessor.reference.ChildReference;
\r
41 import org.simantics.databoard.accessor.reference.IndexReference;
\r
42 import org.simantics.databoard.accessor.reference.LabelReference;
\r
43 import org.simantics.databoard.adapter.AdaptException;
\r
44 import org.simantics.databoard.binding.ArrayBinding;
\r
45 import org.simantics.databoard.binding.Binding;
\r
46 import org.simantics.databoard.binding.error.BindingException;
\r
47 import org.simantics.databoard.binding.mutable.MutableVariant;
\r
48 import org.simantics.databoard.serialization.Serializer;
\r
49 import org.simantics.databoard.serialization.SerializerConstructionException;
\r
50 import org.simantics.databoard.type.ArrayType;
\r
51 import org.simantics.databoard.type.Datatype;
\r
52 import org.simantics.databoard.type.LongType;
\r
53 import org.simantics.databoard.util.binary.Blob;
\r
54 import org.simantics.databoard.util.binary.RandomAccessBinary.ByteSide;
\r
57 * Binary Array is accessor to a byte backed array of elements.
\r
60 * Note, To increase the random access performance of the record, create sub-accessors of
\r
63 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
\r
65 public class BinaryVariableWidthStreamArray extends BinaryObject implements ArrayAccessor, FileArrayAccessor, ArrayAccessor.CloseableArrayAccessor, StreamAccessor {
\r
67 /** Accessors to children */
\r
68 TreeMap<Integer, java.lang.ref.Reference<BinaryObject>> children = new TreeMap<Integer, java.lang.ref.Reference<BinaryObject>>();
\r
72 Integer constantSize;
\r
73 ArrayAccessor index;
\r
75 public BinaryVariableWidthStreamArray(BinaryObject parent, Blob blob, Datatype type, AccessorParams params, ArrayAccessor index)
\r
76 throws AccessorConstructionException
\r
78 super(parent, blob, type, params);
\r
79 ArrayType at = (ArrayType) type;
\r
80 cb = params.bindingScheme.getBindingUnchecked(at.componentType);
\r
81 cs = params.serializerScheme.getSerializerUnchecked( cb );
\r
82 constantSize = cs.getConstantSize();
\r
83 if (index==null || index.type().componentType instanceof LongType == false) {
\r
84 throw new AccessorConstructionException("Index must be Long[]");
\r
89 public ArrayType type() {
\r
90 return (ArrayType) type;
\r
94 * Get existing sub accessor
\r
96 * @return sub-accessor or <code>null</code>
\r
98 BinaryObject getExistingAccessor(int index)
\r
100 java.lang.ref.Reference<BinaryObject> ref = children.get(index);
\r
101 if (ref==null) return null;
\r
102 BinaryObject res = (BinaryObject) ref.get();
\r
103 // if (res==null) children.remove(index);
\r
108 * Get start position of a field
\r
110 * @param fieldIndex
\r
112 * @throws AccessorException
\r
114 long getStartPosition(int fieldIndex) throws AccessorException {
\r
115 int c = this.index.size();
\r
116 if ( fieldIndex==c )
\r
119 } catch (IOException e) {
\r
120 throw new AccessorException( e );
\r
122 if ( fieldIndex>c || fieldIndex<0 ) throw new AccessorException("Index out of bounds");
\r
123 return (Long) index.get(fieldIndex, Bindings.LONG);
\r
126 long getLength(int index, long pos) throws AccessorException {
\r
127 int c = this.index.size();
\r
128 if ( index>=c || index<0 ) return 0;
\r
132 return b.length() - (Long) this.index.get(c-1, Bindings.LONG);
\r
133 } catch (IOException e) {
\r
134 throw new AccessorException( e );
\r
137 return (Long) this.index.get(index+1, Bindings.LONG) - (Long) this.index.get(index, Bindings.LONG);
\r
142 public void setNoflush(int index, Binding rcb, Object rcv)
\r
143 throws AccessorException {
\r
149 int count = size();
\r
150 if ( index==count ) {
\r
151 addNoflush(count, rcb, rcv);
\r
156 Serializer rcs = params.serializerScheme.getSerializer( rcb );
\r
157 long pos = getStartPosition(index);
\r
158 long oldSize = getLength(index, pos);
\r
159 long newSize = rcs.getSize(rcv, null);
\r
161 long diff = newSize - oldSize;
\r
163 b.insertBytes( newSize - oldSize, ByteSide.Right );
\r
164 } else if (diff<0) {
\r
165 b.removeBytes( oldSize - newSize, ByteSide.Right );
\r
168 rcs.serialize(b, null, rcv);
\r
170 // Update index of the higher indices
\r
172 for (int i=index+1; i<count; i++) {
\r
173 long p = (Long) this.index.get(i, Bindings.LONG);
\r
175 this.index.set(i, Bindings.LONG, p);
\r
181 BinaryObject sa = getExistingAccessor(index);
\r
182 if (sa!=null && newSize != oldSize) {
\r
183 sa.b.setPositionInSource(pos, newSize);
\r
186 // Shift consecutive blobs
\r
187 /* if (!children.isEmpty()) {
\r
188 Integer lastKey = children.lastKey();
\r
189 if (lastKey!=null && index+1 <= lastKey && diff!=0) {
\r
190 SortedMap<Integer, Reference<BinaryObject>> pm = children.subMap(index+1, true, lastKey, true);
\r
191 for (Entry<Integer, Reference<BinaryObject>> e : pm.entrySet()) {
\r
192 BinaryObject sa_ = e.getValue().get();
\r
193 if (sa_ == null) continue;
\r
194 sa_.b.setPositionInSource( sa_.b.getStartPositionInSourceBinary() + diff, sa_.b.length());
\r
200 ListenerEntry le = listeners;
\r
201 while (le!=null) {
\r
202 ArrayInterestSet is = le.getInterestSet();
\r
203 if (is.inNotificationsOf(index)) {
\r
204 MutableVariant newValue = null;
\r
205 if (is.inValues()) newValue = new MutableVariant(rcb, rcb.isImmutable() ? rcv : rcb.clone(rcv));
\r
207 Event e = new ValueAssigned(new IndexReference(index), newValue);
\r
213 } catch (IOException e) {
\r
214 throw new AccessorException(e);
\r
215 } catch (AdaptException e) {
\r
216 throw new AccessorException(e);
\r
217 } catch (SerializerConstructionException e) {
\r
218 throw new AccessorException(e);
\r
228 * @param arrayBinding
\r
232 public void setValueNoflush(Binding arrayBinding, Object newArray)
\r
233 throws AccessorException {
\r
238 ArrayBinding rb = ((ArrayBinding)arrayBinding);
\r
239 Binding rcb = rb.getComponentBinding();
\r
240 Serializer rcs = params.serializerScheme.getSerializer( rcb );
\r
241 int oldCount = index.size();
\r
242 int newCount = rb.size(newArray);
\r
243 b.setLength( params.serializerScheme.getSerializer( rb ).getSize(newArray) );
\r
244 index.setSize(newCount);
\r
247 for (int index=0; index<newCount; index++) {
\r
248 //long startPos = b.position();
\r
249 Object obj = rb.get(newArray, index);
\r
250 this.index.set(index, Bindings.LONG, b.position());
\r
251 rcs.serialize(b, obj);
\r
252 //long endPos = b.position();
\r
253 //long len = endPos - startPos;
\r
257 BinaryObject sa = getExistingAccessor(index);
\r
259 sa.b.setPositionInSource(startPos, len);
\r
264 for (int index=oldCount-1; index>=newCount; index--) {
\r
265 BinaryObject sa = getExistingAccessor(index);
\r
267 sa.invalidatedNotification();
\r
268 children.remove(index);
\r
273 ListenerEntry le = listeners;
\r
274 while (le!=null) {
\r
275 ArrayInterestSet is = le.getInterestSet();
\r
276 if (is.inNotificationsOf(index)) {
\r
277 Event e = new ArrayElementRemoved(index);
\r
284 // Notify new assignment
\r
285 if (listeners!=null) {
\r
286 for (int index=0; index<newCount; index++) {
\r
287 Object cv = rb.get(newArray, index);
\r
290 ListenerEntry le = listeners;
\r
291 while (le!=null) {
\r
292 ArrayInterestSet is = le.getInterestSet();
\r
293 if (is.inNotificationsOf(index)) {
\r
294 MutableVariant vv = null;
\r
295 if (is.inValues()) vv = new MutableVariant(rcb, cb.isImmutable() ? cv : rcb.clone(cv));
\r
297 Event e = index<oldCount ? new ValueAssigned(new IndexReference(index), vv) : new ArrayElementAdded(index, vv);
\r
305 } catch (BindingException e) {
\r
306 throw new AccessorException(e);
\r
307 } catch (IOException e) {
\r
308 throw new AccessorException(e);
\r
309 } catch (AdaptException e) {
\r
310 throw new AccessorException(e);
\r
311 } catch (SerializerConstructionException e) {
\r
312 throw new AccessorException(e);
\r
320 public void addNoflush(int index, Binding rcb, Object rcv) throws AccessorException {
\r
324 Serializer rcs = params.serializerScheme.getSerializer( rcb );
\r
326 int oldCount = this.index.size();
\r
327 int newCount = oldCount+1;
\r
328 boolean lastEntry = index == oldCount;
\r
329 if (index>oldCount) throw new AccessorException("Index out of range");
\r
331 long pos = getStartPosition(index);
\r
332 int size = rcs.getSize(rcv);
\r
334 b.insertBytes(size, ByteSide.Left);
\r
335 rcs.serialize(b, null, rcv);
\r
337 this.index.add(index, Bindings.LONG, pos);
\r
339 for (int i=index+1; i<newCount; i++) {
\r
340 long p = (Long) this.index.get(i, Bindings.LONG);
\r
342 this.index.set(i, Bindings.LONG, p);
\r
346 // Update child map keys
\r
347 if (!lastEntry && !children.isEmpty()) {
\r
348 Integer key = children.lastKey();
\r
349 while (key != null && key >= index) {
\r
350 java.lang.ref.Reference<BinaryObject> value = children.remove(key);
\r
351 if (value.get()!=null) children.put(key+1, value);
\r
352 key = children.lowerKey(key);
\r
356 // Notify Listeners
\r
357 ListenerEntry le = listeners;
\r
358 while (le!=null) {
\r
359 ArrayInterestSet is = le.getInterestSet();
\r
360 if (is.inNotifications()) {
\r
361 MutableVariant newValue = null;
\r
362 if (is.inValues()) newValue = new MutableVariant(rcb, rcb.isImmutable() ? rcv : rcb.clone(rcv));
\r
363 ArrayElementAdded e = new ArrayElementAdded(index, newValue);
\r
367 // Update indices of interest sets
\r
368 if (is.componentInterests!=null) {
\r
369 Map<Integer, InterestSet> oldCis = is.componentInterests;
\r
370 boolean needUpdates = false;
\r
371 for (Integer i : oldCis.keySet()) {
\r
372 needUpdates |= i>=index;
\r
373 if (needUpdates) break;
\r
377 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size());
\r
378 for (Integer i : oldCis.keySet())
\r
380 Integer oldKey = i;
\r
381 Integer newKey = i>=index ? i+1 : i;
\r
382 InterestSet oldValue = oldCis.get(oldKey);
\r
383 newCis.put(newKey, oldValue);
\r
385 is.componentInterests = newCis;
\r
389 // Add component interest listener
\r
391 boolean hadSa = getExistingAccessor(index) != null;
\r
393 // Add component interest listener
\r
394 InterestSet cis = is.getComponentInterest();
\r
396 Accessor sa = getAccessor(index);
\r
398 cis = is.getComponentInterest(index);
\r
400 Accessor sa = getAccessor(index);
\r
407 } catch (IOException e) {
\r
408 throw new AccessorException(e);
\r
409 } catch (AdaptException e) {
\r
410 throw new AccessorException(e);
\r
411 } catch (SerializerConstructionException e) {
\r
412 throw new AccessorException(e);
\r
419 public void addNoflush(Binding binding, Object value)
\r
420 throws AccessorException {
\r
421 addNoflush(size(), binding, value);
\r
425 public void addAllNoflush(Binding binding, Object[] values)
\r
426 throws AccessorException {
\r
427 addAllNoflush(size(), binding, values);
\r
431 public void addAllNoflush(int index, Binding rcb, Object[] rcvs)
\r
432 throws AccessorException {
\r
433 if (index<0||index>size()) throw new AccessorException("Index out of bounds");
\r
437 Serializer rcs = params.serializerScheme.getSerializer( rcb );
\r
440 int repeatCount = rcvs.length;
\r
441 int oldCount = b.readInt();
\r
442 int newCount = oldCount + rcvs.length;
\r
443 if (index>oldCount) throw new AccessorException("Index out of range");
\r
444 boolean lastEntry = index == oldCount;
\r
447 for (int i=0; i<rcvs.length; i++)
\r
448 size += rcs.getSize(rcvs[i]);
\r
449 long pos = getStartPosition(index);
\r
451 b.insertBytes(size, ByteSide.Right);
\r
454 for (int i=0; i<repeatCount; i++) {
\r
455 this.index.add(index+i, Bindings.LONG, (Long) p);
\r
456 p += rcs.getSize(rcvs[i]);
\r
458 for (int i=index+repeatCount; i<newCount; i++) {
\r
459 p = (Long) this.index.get(i, Bindings.LONG);
\r
461 this.index.set(i, Bindings.LONG, p);
\r
465 for (int i=0; i<rcvs.length; i++) {
\r
466 Object rcv = rcvs[i];
\r
467 rcs.serialize(b, rcv);
\r
470 // Update child map keys
\r
471 if (!lastEntry && !children.isEmpty()) {
\r
472 Integer key = children.lastKey();
\r
473 while (key!=null && key >= index) {
\r
474 java.lang.ref.Reference<BinaryObject> value = children.remove(key);
\r
475 if (value.get()!=null) children.put(key+rcvs.length, value);
\r
476 key = children.lowerKey(key);
\r
480 // Notify Listeners
\r
481 ListenerEntry le = listeners;
\r
482 while (le!=null) {
\r
483 ArrayInterestSet is = le.getInterestSet();
\r
484 if (is.inNotifications()) {
\r
485 for (int i=0; i<rcvs.length; i++) {
\r
486 MutableVariant newValue = null;
\r
487 if (is.inValues()) newValue = new MutableVariant(rcb, rcb.isImmutable() ? rcvs[i] : cb.clone(rcvs[i]));
\r
488 ArrayElementAdded e = new ArrayElementAdded(index, newValue);
\r
493 // Update indices of interest sets
\r
494 if (is.componentInterests!=null) {
\r
495 Map<Integer, InterestSet> oldCis = is.componentInterests;
\r
496 boolean needUpdates = false;
\r
497 for (Integer i : oldCis.keySet()) {
\r
498 needUpdates |= i>=index;
\r
499 if (needUpdates) break;
\r
503 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size());
\r
504 for (Integer i : oldCis.keySet())
\r
506 Integer oldKey = i;
\r
507 Integer newKey = i>=index ? i+rcvs.length : i;
\r
508 InterestSet oldValue = oldCis.get(oldKey);
\r
509 newCis.put(newKey, oldValue);
\r
511 is.componentInterests = newCis;
\r
514 // Add component interest listener
\r
516 for (int i = index; i<index+rcvs.length; i++) {
\r
517 boolean hadSa = getExistingAccessor(i)!=null;
\r
518 if (hadSa) continue;
\r
520 InterestSet cis = is.getComponentInterest();
\r
522 Accessor sa = getAccessor(i);
\r
524 cis = is.getComponentInterest(index);
\r
526 Accessor sa = getAccessor(i);
\r
536 } catch (IOException e) {
\r
537 throw new AccessorException(e);
\r
538 } catch (AdaptException e) {
\r
539 throw new AccessorException(e);
\r
540 } catch (SerializerConstructionException e) {
\r
541 throw new AccessorException(e);
\r
547 void addRepeatNoflush(int index, Binding rcb, Object obj, int repeatCount) throws AccessorException {
\r
548 if (index<0||index>size()) throw new AccessorException("Index out of bounds");
\r
552 Serializer rcs = params.serializerScheme.getSerializer( rcb );
\r
555 int oldCount = this.index.size();
\r
556 int newCount = oldCount + repeatCount;
\r
557 if (index>oldCount) throw new AccessorException("Index out of range");
\r
558 boolean lastEntry = index == oldCount;
\r
560 long componentSize = rcs.getSize(obj);
\r
561 long size = componentSize * repeatCount;
\r
562 long pos = getStartPosition(index);
\r
564 b.insertBytes(size, ByteSide.Right);
\r
566 for (int i=0; i<repeatCount; i++) {
\r
567 this.index.add(index+i, Bindings.LONG, (Long) p);
\r
568 pos += componentSize;
\r
570 for (int i=index+repeatCount; i<newCount; i++) {
\r
571 p = (Long) this.index.get(i, Bindings.LONG);
\r
573 this.index.set(i, Bindings.LONG, p);
\r
577 for (int i=0; i<repeatCount; i++) {
\r
578 rcs.serialize(b, obj);
\r
581 // Update child map keys
\r
582 if (!lastEntry && !children.isEmpty()) {
\r
583 Integer key = children.lastKey();
\r
584 while (key!=null && key >= index) {
\r
585 java.lang.ref.Reference<BinaryObject> value = children.remove(key);
\r
586 if (value.get()!=null) children.put(key+repeatCount, value);
\r
587 key = children.lowerKey(key);
\r
591 // Notify Listeners
\r
592 ListenerEntry le = listeners;
\r
593 while (le!=null) {
\r
594 ArrayInterestSet is = le.getInterestSet();
\r
595 if (is.inNotifications()) {
\r
596 for (int i=0; i<repeatCount; i++) {
\r
597 MutableVariant newValue = null;
\r
598 if (is.inValues()) newValue = new MutableVariant(rcb, obj/*rcb.isImmutable() ? obj : cb.clone(obj)*/);
\r
599 ArrayElementAdded e = new ArrayElementAdded(index, newValue);
\r
604 // Update indices of interest sets
\r
605 if (is.componentInterests!=null) {
\r
606 Map<Integer, InterestSet> oldCis = is.componentInterests;
\r
607 boolean needUpdates = false;
\r
608 for (Integer i : oldCis.keySet()) {
\r
609 needUpdates |= i>=index;
\r
610 if (needUpdates) break;
\r
614 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size());
\r
615 for (Integer i : oldCis.keySet())
\r
617 Integer oldKey = i;
\r
618 Integer newKey = i>=index ? i+repeatCount : i;
\r
619 InterestSet oldValue = oldCis.get(oldKey);
\r
620 newCis.put(newKey, oldValue);
\r
622 is.componentInterests = newCis;
\r
630 } catch (IOException e) {
\r
631 throw new AccessorException(e);
\r
632 } catch (SerializerConstructionException e) {
\r
633 throw new AccessorException(e);
\r
640 public void removeNoflush(int index, int count) throws AccessorException {
\r
645 boolean lastEntry = index == count;
\r
646 int oldCount = this.index.size();
\r
647 int newCount = oldCount - count;
\r
648 if (index<0||index+count>oldCount) throw new AccessorException("Index out of bounds");
\r
649 long pos = getStartPosition(index);
\r
650 long lastPos = getStartPosition(index+count-1);
\r
651 long lastLen = getLength(index, lastPos);
\r
652 long end = lastPos + lastLen;
\r
653 long len = end - pos;
\r
655 b.removeBytes(len, ByteSide.Right);
\r
656 this.index.remove(index, count);
\r
657 for (int i=index; i<newCount; i++) {
\r
658 long p = (Long) this.index.get(i, Bindings.LONG);
\r
660 this.index.set(i, Bindings.LONG, p);
\r
664 SortedMap<Integer, java.lang.ref.Reference<BinaryObject>> sm = children.subMap(index, true, index+count, false);
\r
665 for (Entry<Integer, java.lang.ref.Reference<BinaryObject>> e : sm.entrySet()) {
\r
666 BinaryObject bo = e.getValue().get();
\r
667 if (bo==null) continue;
\r
668 bo.invalidatedNotification();
\r
672 // Update the keys of consecutive children
\r
673 if (!lastEntry && !children.isEmpty()) {
\r
674 Integer lastKey = children.lastKey();
\r
675 Integer key = children.higherKey(index);
\r
676 while (key != null && key <= lastKey) {
\r
677 java.lang.ref.Reference<BinaryObject> value = children.remove(key);
\r
678 if (value.get()!=null) children.put(key-count, value);
\r
679 key = children.higherKey(key);
\r
683 // Notify Listeners
\r
684 ListenerEntry le = listeners;
\r
685 while (le!=null) {
\r
686 ArrayInterestSet is = le.getInterestSet();
\r
687 if (is.inNotifications()) {
\r
688 ArrayElementRemoved e = new ArrayElementRemoved(index);
\r
692 // Update indices of interest sets
\r
693 if (is.componentInterests!=null) {
\r
694 Map<Integer, InterestSet> oldCis = is.componentInterests;
\r
695 boolean needUpdates = false;
\r
696 for (Integer i : oldCis.keySet()) {
\r
697 needUpdates |= i>=index;
\r
698 if (needUpdates) break;
\r
702 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size());
\r
703 for (Integer i : oldCis.keySet())
\r
705 Integer oldKey = i;
\r
706 Integer newKey = i>=index ? i-1 : i;
\r
707 InterestSet oldValue = oldCis.get(oldKey);
\r
708 newCis.put(newKey, oldValue);
\r
710 is.componentInterests = newCis;
\r
717 } catch (IOException e) {
\r
718 throw new AccessorException( e );
\r
725 public Object get(int index, Binding valueBinding) throws AccessorException {
\r
729 long pos = getStartPosition(index);
\r
731 Serializer s = params.serializerScheme.getSerializer(valueBinding);
\r
732 return s.deserialize(b);
\r
733 } catch (IOException e) {
\r
734 throw new AccessorException(e);
\r
735 } catch (SerializerConstructionException e) {
\r
736 throw new AccessorException(e);
\r
743 public void get(int index, Binding valueBinding, Object dst) throws AccessorException {
\r
747 long pos = getStartPosition(index);
\r
749 Serializer s = params.serializerScheme.getSerializer(valueBinding);
\r
750 s.deserializeTo(b, dst);
\r
751 } catch (IOException e) {
\r
752 throw new AccessorException(e);
\r
753 } catch (SerializerConstructionException e) {
\r
754 throw new AccessorException(e);
\r
761 @SuppressWarnings("unchecked")
\r
763 public <T extends Accessor> T getAccessor(int index)
\r
764 throws AccessorConstructionException {
\r
769 int count = b.readInt();
\r
770 if (index<0 || index>=count) throw new ReferenceException("Element index ("+index+") out of bounds ("+count+")");
\r
772 // Get existing or create new
\r
773 BinaryObject sa = getExistingAccessor(index);
\r
775 long pos = getStartPosition(index);
\r
776 long len = getLength(index, pos);
\r
778 // Instantiate correct sub accessor.
\r
779 sa = createSubAccessor(cb.type(), pos, len, params);
\r
780 children.put(index, new WeakReference<BinaryObject>(sa) );
\r
782 // Add component interest sets
\r
783 ListenerEntry le = listeners;
\r
784 while (le!=null) {
\r
785 ArrayInterestSet is = le.getInterestSet();
\r
787 // Generic element interest
\r
788 InterestSet gis = is.getComponentInterest();
\r
791 ChildReference childPath = ChildReference.concatenate(le.path, new IndexReference(index) );
\r
792 sa.addListener(le.listener, gis, childPath, le.executor);
\r
793 } catch (AccessorException e) {
\r
794 throw new AccessorConstructionException(e);
\r
798 // Specific element interest
\r
799 InterestSet cis = is.getComponentInterest(index);
\r
802 ChildReference childPath = ChildReference.concatenate(le.path, new IndexReference(index) );
\r
803 sa.addListener(le.listener, cis, childPath,le.executor);
\r
804 } catch (AccessorException e) {
\r
805 throw new AccessorConstructionException(e);
\r
816 } catch (IOException e) {
\r
817 throw new AccessorConstructionException(e);
\r
818 } catch (AccessorException e) {
\r
819 throw new AccessorConstructionException(e);
\r
825 @SuppressWarnings("unchecked")
\r
827 public <T extends Accessor> T getComponent(ChildReference reference)
\r
828 throws AccessorConstructionException {
\r
829 if (reference==null) return (T) this;
\r
830 if (reference instanceof LabelReference) {
\r
831 LabelReference lr = (LabelReference) reference;
\r
833 Integer index = new Integer( lr.label );
\r
834 Accessor result = getAccessor(index);
\r
835 if (reference.getChildReference() != null)
\r
836 result = result.getComponent(reference.getChildReference());
\r
838 } catch ( NumberFormatException nfe ) {
\r
839 throw new ReferenceException(nfe);
\r
841 } else if (reference instanceof IndexReference) {
\r
842 IndexReference ref = (IndexReference) reference;
\r
843 int index = ref.getIndex();
\r
844 Accessor result = getAccessor(index);
\r
845 if (reference.getChildReference() != null)
\r
846 result = result.getComponent(reference.getChildReference());
\r
848 } throw new ReferenceException(reference.getClass().getName()+" is not a reference of an array");
\r
852 public void getAll(Binding valueBinding, Object[] array)
\r
853 throws AccessorException {
\r
858 int size = b.readInt();
\r
859 if (size > array.length) throw new AccessorException("Argument array too short");
\r
860 Serializer s = params.serializerScheme.getSerializer(valueBinding);
\r
861 for (int i=0; i<size; i++) {
\r
862 array[i] = s.deserialize(b);
\r
864 } catch (IOException e) {
\r
865 throw new AccessorException( e );
\r
866 } catch (SerializerConstructionException e) {
\r
867 throw new AccessorException( e );
\r
874 public void getAll(Binding valueBinding, Collection<Object> values)
\r
875 throws AccessorException {
\r
880 int size = b.readInt();
\r
881 Serializer s = params.serializerScheme.getSerializer(valueBinding);
\r
882 for (int i=0; i<size; i++) {
\r
883 values.add( s.deserialize(b) );
\r
885 } catch (IOException e) {
\r
886 throw new AccessorException( e );
\r
887 } catch (SerializerConstructionException e) {
\r
888 throw new AccessorException( e );
\r
895 public void setSizeNoflush(int newSize) throws AccessorException {
\r
899 int oldSize = size();
\r
901 // Remove instances
\r
902 if (newSize<oldSize) {
\r
903 remove(newSize, oldSize-newSize);
\r
906 // Add dummy instances
\r
907 if (newSize>oldSize) {
\r
908 Object dummy = cb.createDefault();
\r
909 int count = newSize-oldSize;
\r
910 addRepeatNoflush(oldSize, cb, dummy, count);
\r
913 } catch (BindingException e) {
\r
914 throw new AccessorException( e );
\r
921 public void setSize(int newSize) throws AccessorException {
\r
925 setSizeNoflush(newSize);
\r
927 } catch (IOException e) {
\r
928 throw new AccessorException( e );
\r
935 public int size() throws AccessorException {
\r
938 return index.size();
\r
942 Event applyLocal(Event e, boolean makeRollback) throws AccessorException {
\r
943 Event rollback = null;
\r
944 if (e instanceof ValueAssigned) {
\r
945 ValueAssigned va = (ValueAssigned) e;
\r
946 if (makeRollback) rollback = new ValueAssigned(cb, getValue(cb));
\r
947 setValueNoflush(va.newValue.getBinding(), va.newValue.getValue());
\r
949 if (e instanceof ArrayElementAdded) {
\r
950 ArrayElementAdded aa = (ArrayElementAdded) e;
\r
951 addNoflush(aa.index, aa.value.getBinding(), aa.value.getValue());
\r
952 if (makeRollback) rollback = new ArrayElementRemoved(aa.index);
\r
953 } else if (e instanceof ArrayElementRemoved) {
\r
954 ArrayElementRemoved ar = (ArrayElementRemoved) e;
\r
955 if (ar.index<0 || ar.index >=size()) throw new AccessorException("Array index out of bounds");
\r
956 if (makeRollback) {
\r
957 Object cv = get(ar.index, cb);
\r
958 rollback = new ArrayElementAdded(ar.index, new MutableVariant(cb, cv));
\r
960 removeNoflush(ar.index, 1);
\r
962 throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Array");
\r
969 public void add(Binding binding, Object value) throws AccessorException {
\r
973 addNoflush(binding, value);
\r
975 } catch (IOException e) {
\r
976 throw new AccessorException( e );
\r
983 public void add(int index, Binding binding, Object value)
\r
984 throws AccessorException {
\r
988 addNoflush(index, binding, value);
\r
990 } catch (IOException e) {
\r
991 throw new AccessorException( e );
\r
998 public void addAll(Binding binding, Object[] values)
\r
999 throws AccessorException {
\r
1000 assert b.isOpen();
\r
1003 addAllNoflush(binding, values);
\r
1005 } catch (IOException e) {
\r
1006 throw new AccessorException( e );
\r
1013 public void addAll(int index, Binding binding, Object[] values)
\r
1014 throws AccessorException {
\r
1015 assert b.isOpen();
\r
1018 addAllNoflush(index, binding, values);
\r
1020 } catch (IOException e) {
\r
1021 throw new AccessorException( e );
\r
1028 public void remove(int index, int count) throws AccessorException {
\r
1029 assert b.isOpen();
\r
1032 removeNoflush(index, count);
\r
1034 } catch (IOException e) {
\r
1035 throw new AccessorException( e );
\r
1042 public void set(int index, Binding binding, Object value)
\r
1043 throws AccessorException {
\r
1044 assert b.isOpen();
\r
1047 setNoflush(index, binding, value);
\r
1049 } catch (IOException e) {
\r
1050 throw new AccessorException(e);
\r
1057 public void close() throws AccessorException {
\r
1058 if ( this.index instanceof CloseableAccessor ) {
\r
1059 CloseableAccessor ca = (CloseableAccessor) this.index;
\r