1 /*******************************************************************************
2 * Copyright (c) 2007, 2011 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.CloseableAccessor;
26 import org.simantics.databoard.accessor.StreamAccessor;
27 import org.simantics.databoard.accessor.error.AccessorConstructionException;
28 import org.simantics.databoard.accessor.error.AccessorException;
29 import org.simantics.databoard.accessor.error.ReferenceException;
30 import org.simantics.databoard.accessor.event.ArrayElementAdded;
31 import org.simantics.databoard.accessor.event.ArrayElementRemoved;
32 import org.simantics.databoard.accessor.event.Event;
33 import org.simantics.databoard.accessor.event.ValueAssigned;
34 import org.simantics.databoard.accessor.file.FileArrayAccessor;
35 import org.simantics.databoard.accessor.impl.AccessorParams;
36 import org.simantics.databoard.accessor.impl.ListenerEntry;
37 import org.simantics.databoard.accessor.interestset.ArrayInterestSet;
38 import org.simantics.databoard.accessor.interestset.InterestSet;
39 import org.simantics.databoard.accessor.reference.ChildReference;
40 import org.simantics.databoard.accessor.reference.IndexReference;
41 import org.simantics.databoard.accessor.reference.LabelReference;
42 import org.simantics.databoard.adapter.AdaptException;
43 import org.simantics.databoard.binding.ArrayBinding;
44 import org.simantics.databoard.binding.Binding;
45 import org.simantics.databoard.binding.error.BindingException;
46 import org.simantics.databoard.binding.mutable.MutableVariant;
47 import org.simantics.databoard.serialization.Serializer;
48 import org.simantics.databoard.serialization.SerializerConstructionException;
49 import org.simantics.databoard.type.ArrayType;
50 import org.simantics.databoard.type.Datatype;
51 import org.simantics.databoard.util.binary.Blob;
52 import org.simantics.databoard.util.binary.RandomAccessBinary.ByteSide;
55 * Stream Array is accessor to an array where values can be added to
56 * the end, and where element size is constant.
59 * The Binary format is different from the normal array format, there is no size
60 * integer at the beginning of the binary data. Instead the size is derieved
61 * from the size of the binary data by dividing byte size with element size.
62 * Therefore, the element size must be constant.
64 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
66 public class BinaryStreamArray extends BinaryObject implements ArrayAccessor, FileArrayAccessor, CloseableAccessor, ArrayAccessor.CloseableArrayAccessor, StreamAccessor {
68 /** Accessors to children */
69 TreeMap<Integer, java.lang.ref.Reference<BinaryObject>> children = new TreeMap<Integer, java.lang.ref.Reference<BinaryObject>>();
71 /** Element Binding */
73 /** Element Serializer */
78 public BinaryStreamArray(BinaryObject parent, Blob blob, Datatype type, AccessorParams params)
79 throws AccessorException
81 super(parent, blob, type, params);
82 ArrayType at = (ArrayType) type;
83 cb = params.bindingScheme.getBindingUnchecked(at.componentType);
84 cs = params.serializerScheme.getSerializerUnchecked( cb );
85 Integer elementConstantSize = cs.getConstantSize();
86 if (elementConstantSize == null) {
87 throw new AccessorException("The size in an element of an AppendableArray must be constant.");
89 elementSize = elementConstantSize;
92 public ArrayType type() {
93 return (ArrayType) type;
98 * Get existing sub accessor
100 * @return sub-accessor or <code>null</code>
102 BinaryObject getExistingAccessor(int index)
104 java.lang.ref.Reference<BinaryObject> ref = children.get(index);
105 if (ref==null) return null;
106 BinaryObject res = (BinaryObject) ref.get();
107 // if (res==null) children.remove(index);
112 * Get start position of a field
116 * @throws AccessorException
118 long getStartPosition(int fieldIndex) throws AccessorException {
119 return fieldIndex * (long) (elementSize);
122 long getLength(int index, long pos) throws AccessorException {
127 public void setNoflush(int index, Binding rcb, Object rcv)
128 throws AccessorException {
134 Serializer rcs = params.serializerScheme.getSerializer( rcb );
135 long pos = getStartPosition(index);
137 rcs.serialize(b, null, rcv);
140 ListenerEntry le = listeners;
142 ArrayInterestSet is = le.getInterestSet();
143 if (is.inNotificationsOf(index)) {
144 MutableVariant newValue = null;
145 if (is.inValues()) newValue = new MutableVariant(rcb, rcb.isImmutable() ? rcv : rcb.clone(rcv));
147 Event e = new ValueAssigned(new IndexReference(index), newValue);
153 } catch (IOException e) {
154 throw new AccessorException(e);
155 } catch (AdaptException e) {
156 throw new AccessorException(e);
157 } catch (SerializerConstructionException e) {
158 throw new AccessorException(e);
168 * @param arrayBinding
172 public void setValueNoflush(Binding arrayBinding, Object newArray)
173 throws AccessorException {
178 ArrayBinding rb = ((ArrayBinding)arrayBinding);
179 Binding rcb = rb.getComponentBinding();
180 Serializer rcs = params.serializerScheme.getSerializer( rcb );
181 int oldCount = _size();
182 int newCount = rb.size(newArray);
183 b.setLength( newCount * elementSize );
187 for (int index=0; index<newCount; index++) {
188 Object obj = rb.get(newArray, index);
189 rcs.serialize(b, obj);
193 for (int index=oldCount-1; index>=newCount; index--) {
194 BinaryObject sa = getExistingAccessor(index);
196 sa.invalidatedNotification();
197 children.remove(index);
202 ListenerEntry le = listeners;
204 ArrayInterestSet is = le.getInterestSet();
205 if (is.inNotificationsOf(index)) {
206 Event e = new ArrayElementRemoved(index);
213 // Notify new assignment
214 if (listeners!=null) {
215 for (int index=0; index<newCount; index++) {
216 Object cv = rb.get(newArray, index);
219 ListenerEntry le = listeners;
221 ArrayInterestSet is = le.getInterestSet();
222 if (is.inNotificationsOf(index)) {
223 MutableVariant vv = null;
224 if (is.inValues()) vv = new MutableVariant(rcb, cb.isImmutable() ? cv : rcb.clone(cv));
226 Event e = index<oldCount ? new ValueAssigned(new IndexReference(index), vv) : new ArrayElementAdded(index, vv);
234 } catch (BindingException e) {
235 throw new AccessorException(e);
236 } catch (IOException e) {
237 throw new AccessorException(e);
238 } catch (AdaptException e) {
239 throw new AccessorException(e);
240 } catch (SerializerConstructionException e) {
241 throw new AccessorException(e);
249 public void addNoflush(int index, Binding rcb, Object rcv) throws AccessorException {
253 Serializer rcs = params.serializerScheme.getSerializer( rcb );
255 int oldCount = _size();
256 boolean lastEntry = index == oldCount;
257 if (index>oldCount) throw new AccessorException("Index out of range");
259 long pos = getStartPosition(index);
261 b.insertBytes(elementSize, ByteSide.Left);
262 rcs.serialize(b, null, rcv);
264 // Update child map keys
265 if (!lastEntry && !children.isEmpty()) {
266 Integer key = children.lastKey();
267 while (key != null && key >= index) {
268 java.lang.ref.Reference<BinaryObject> value = children.remove(key);
269 if (value.get()!=null) children.put(key+1, value);
270 key = children.lowerKey(key);
275 ListenerEntry le = listeners;
277 ArrayInterestSet is = le.getInterestSet();
278 if (is.inNotifications()) {
279 MutableVariant newValue = null;
280 if (is.inValues()) newValue = new MutableVariant(rcb, rcb.isImmutable() ? rcv : rcb.clone(rcv));
281 ArrayElementAdded e = new ArrayElementAdded(index, newValue);
285 // Update indices of interest sets
286 if (is.componentInterests!=null) {
287 Map<Integer, InterestSet> oldCis = is.componentInterests;
288 boolean needUpdates = false;
289 for (Integer i : oldCis.keySet()) {
290 needUpdates |= i>=index;
291 if (needUpdates) break;
295 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size());
296 for (Integer i : oldCis.keySet())
299 Integer newKey = i>=index ? i+1 : i;
300 InterestSet oldValue = oldCis.get(oldKey);
301 newCis.put(newKey, oldValue);
303 is.componentInterests = newCis;
310 } catch (IOException e) {
311 throw new AccessorException(e);
312 } catch (AdaptException e) {
313 throw new AccessorException(e);
314 } catch (SerializerConstructionException e) {
315 throw new AccessorException(e);
322 public void addNoflush(Binding binding, Object value)
323 throws AccessorException {
324 addNoflush(_size(), binding, value);
328 public void addAllNoflush(Binding binding, Object[] values)
329 throws AccessorException {
330 addAllNoflush(size(), binding, values);
334 public void addAllNoflush(int index, Binding rcb, Object[] rcvs)
335 throws AccessorException {
336 if (index<0||index>size()) throw new AccessorException("Index out of bounds");
340 Serializer rcs = params.serializerScheme.getSerializer( rcb );
343 int oldCount = b.readInt();
344 int newCount = oldCount + rcvs.length;
345 if (index>oldCount) throw new AccessorException("Index out of range");
347 b.writeInt(newCount);
348 boolean lastEntry = index == oldCount;
351 for (int i=0; i<rcvs.length; i++)
352 size += rcs.getSize(rcvs[i]);
353 long pos = getStartPosition(index);
355 b.insertBytes(size, ByteSide.Right);
358 for (int i=0; i<rcvs.length; i++) {
359 Object rcv = rcvs[i];
360 rcs.serialize(b, rcv);
363 // Update child map keys
364 if (!lastEntry && !children.isEmpty()) {
365 Integer key = children.lastKey();
366 while (key!=null && key >= index) {
367 java.lang.ref.Reference<BinaryObject> value = children.remove(key);
368 if (value.get()!=null) children.put(key+rcvs.length, value);
369 key = children.lowerKey(key);
374 ListenerEntry le = listeners;
376 ArrayInterestSet is = le.getInterestSet();
377 if (is.inNotifications()) {
378 for (int i=0; i<rcvs.length; i++) {
379 MutableVariant newValue = null;
380 if (is.inValues()) newValue = new MutableVariant(rcb, rcb.isImmutable() ? rcvs[i] : cb.clone(rcvs[i]));
381 ArrayElementAdded e = new ArrayElementAdded(index, newValue);
386 // Update indices of interest sets
387 if (is.componentInterests!=null) {
388 Map<Integer, InterestSet> oldCis = is.componentInterests;
389 boolean needUpdates = false;
390 for (Integer i : oldCis.keySet()) {
391 needUpdates |= i>=index;
392 if (needUpdates) break;
396 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size());
397 for (Integer i : oldCis.keySet())
400 Integer newKey = i>=index ? i+rcvs.length : i;
401 InterestSet oldValue = oldCis.get(oldKey);
402 newCis.put(newKey, oldValue);
404 is.componentInterests = newCis;
407 // Add component interest listener
409 for (int i = index; i<index+rcvs.length; i++) {
410 boolean hadSa = getExistingAccessor(i)!=null;
413 InterestSet cis = is.getComponentInterest();
415 Accessor sa = getAccessor(i);
417 cis = is.getComponentInterest(index);
419 Accessor sa = getAccessor(i);
429 } catch (IOException e) {
430 throw new AccessorException(e);
431 } catch (AdaptException e) {
432 throw new AccessorException(e);
433 } catch (SerializerConstructionException e) {
434 throw new AccessorException(e);
440 void addRepeatNoflush(int index, Binding rcb, Object obj, int repeatCount) throws AccessorException {
441 if (index<0||index>size()) throw new AccessorException("Index out of bounds");
445 Serializer rcs = params.serializerScheme.getSerializer( rcb );
447 int oldCount = _size();
448 int newCount = oldCount + repeatCount;
449 if (index>oldCount) throw new AccessorException("Index out of range");
451 b.writeInt(newCount);
452 boolean lastEntry = index == oldCount;
454 int size = rcs.getSize(obj) * repeatCount;
455 long pos = getStartPosition(index);
457 b.insertBytes(size, ByteSide.Right);
460 for (int i=0; i<repeatCount; i++) {
461 rcs.serialize(b, obj);
464 // Update child map keys
465 if (!lastEntry && !children.isEmpty()) {
466 Integer key = children.lastKey();
467 while (key!=null && key >= index) {
468 java.lang.ref.Reference<BinaryObject> value = children.remove(key);
469 if (value.get()!=null) children.put(key+repeatCount, value);
470 key = children.lowerKey(key);
475 ListenerEntry le = listeners;
477 ArrayInterestSet is = le.getInterestSet();
478 if (is.inNotifications()) {
479 for (int i=0; i<repeatCount; i++) {
480 MutableVariant newValue = null;
481 if (is.inValues()) newValue = new MutableVariant(rcb, obj/*rcb.isImmutable() ? obj : cb.clone(obj)*/);
482 ArrayElementAdded e = new ArrayElementAdded(index, newValue);
487 // Update indices of interest sets
488 if (is.componentInterests!=null) {
489 Map<Integer, InterestSet> oldCis = is.componentInterests;
490 boolean needUpdates = false;
491 for (Integer i : oldCis.keySet()) {
492 needUpdates |= i>=index;
493 if (needUpdates) break;
497 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size());
498 for (Integer i : oldCis.keySet())
501 Integer newKey = i>=index ? i+repeatCount : i;
502 InterestSet oldValue = oldCis.get(oldKey);
503 newCis.put(newKey, oldValue);
505 is.componentInterests = newCis;
513 } catch (IOException e) {
514 throw new AccessorException(e);
515 } catch (SerializerConstructionException e) {
516 throw new AccessorException(e);
523 public void removeNoflush(int index, int count) throws AccessorException {
528 boolean lastEntry = index == count;
529 int oldCount = _size();
530 if (index<0||index+count>oldCount) throw new AccessorException("Index out of bounds");
531 long pos = getStartPosition(index);
532 long lastPos = getStartPosition(index+count-1);
533 long lastLen = getLength(index, lastPos);
534 long end = lastPos + lastLen;
535 long len = end - pos;
537 b.removeBytes(len, ByteSide.Right);
540 SortedMap<Integer, java.lang.ref.Reference<BinaryObject>> sm = children.subMap(index, true, index+count, false);
541 for (Entry<Integer, java.lang.ref.Reference<BinaryObject>> e : sm.entrySet()) {
542 BinaryObject bo = e.getValue().get();
543 if (bo==null) continue;
544 bo.invalidatedNotification();
548 // Update the keys of consecutive children
549 if (!lastEntry && !children.isEmpty()) {
550 Integer lastKey = children.lastKey();
551 Integer key = children.higherKey(index);
552 while (key != null && key <= lastKey) {
553 java.lang.ref.Reference<BinaryObject> value = children.remove(key);
554 if (value.get()!=null) children.put(key-count, value);
555 key = children.higherKey(key);
560 ListenerEntry le = listeners;
562 ArrayInterestSet is = le.getInterestSet();
563 if (is.inNotifications()) {
564 ArrayElementRemoved e = new ArrayElementRemoved(index);
568 // Update indices of interest sets
569 if (is.componentInterests!=null) {
570 Map<Integer, InterestSet> oldCis = is.componentInterests;
571 boolean needUpdates = false;
572 for (Integer i : oldCis.keySet()) {
573 needUpdates |= i>=index;
574 if (needUpdates) break;
578 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size());
579 for (Integer i : oldCis.keySet())
582 Integer newKey = i>=index ? i-1 : i;
583 InterestSet oldValue = oldCis.get(oldKey);
584 newCis.put(newKey, oldValue);
586 is.componentInterests = newCis;
593 } catch (IOException e) {
594 throw new AccessorException( e );
601 public Object get(int index, Binding valueBinding) throws AccessorException {
605 long pos = getStartPosition(index);
607 Serializer s = params.serializerScheme.getSerializer(valueBinding);
608 return s.deserialize(b);
609 } catch (IOException e) {
610 throw new AccessorException(e);
611 } catch (SerializerConstructionException e) {
612 throw new AccessorException(e);
619 public void get(int index, Binding valueBinding, Object dst) throws AccessorException {
623 long pos = getStartPosition(index);
625 Serializer s = params.serializerScheme.getSerializer(valueBinding);
626 s.deserializeTo(b, dst);
627 } catch (IOException e) {
628 throw new AccessorException(e);
629 } catch (SerializerConstructionException e) {
630 throw new AccessorException(e);
637 @SuppressWarnings("unchecked")
639 public <T extends Accessor> T getAccessor(int index)
640 throws AccessorConstructionException {
645 if (index<0 || index>=count) throw new ReferenceException("Element index ("+index+") out of bounds ("+count+")");
647 // Get existing or create new
648 BinaryObject sa = getExistingAccessor(index);
650 long pos = getStartPosition(index);
651 long len = getLength(index, pos);
653 // Instantiate correct sub accessor.
654 sa = createSubAccessor(cb.type(), pos, len, params);
655 children.put(index, new WeakReference<BinaryObject>(sa) );
657 // Add component interest sets
658 ListenerEntry le = listeners;
660 ArrayInterestSet is = le.getInterestSet();
662 // Generic element interest
663 InterestSet gis = is.getComponentInterest();
666 ChildReference childPath = ChildReference.concatenate(le.path, new IndexReference(index) );
667 sa.addListener(le.listener, gis, childPath, le.executor);
668 } catch (AccessorException e) {
669 throw new AccessorConstructionException(e);
673 // Specific element interest
674 InterestSet cis = is.getComponentInterest(index);
677 ChildReference childPath = ChildReference.concatenate(le.path, new IndexReference(index) );
678 sa.addListener(le.listener, cis, childPath,le.executor);
679 } catch (AccessorException e) {
680 throw new AccessorConstructionException(e);
691 } catch (AccessorException e) {
692 throw new AccessorConstructionException(e);
698 @SuppressWarnings("unchecked")
700 public <T extends Accessor> T getComponent(ChildReference reference)
701 throws AccessorConstructionException {
702 if (reference==null) return (T) this;
703 if (reference instanceof LabelReference) {
704 LabelReference lr = (LabelReference) reference;
706 Integer index = new Integer( lr.label );
707 Accessor result = getAccessor(index);
708 if (reference.getChildReference() != null)
709 result = result.getComponent(reference.getChildReference());
711 } catch ( NumberFormatException nfe ) {
712 throw new ReferenceException(nfe);
714 } else if (reference instanceof IndexReference) {
715 IndexReference ref = (IndexReference) reference;
716 int index = ref.getIndex();
717 Accessor result = getAccessor(index);
718 if (reference.getChildReference() != null)
719 result = result.getComponent(reference.getChildReference());
721 } throw new ReferenceException(reference.getClass().getName()+" is not a reference of an array");
725 public void getAll(Binding valueBinding, Object[] array)
726 throws AccessorException {
731 if (size > array.length) throw new AccessorException("Argument array too short");
732 Serializer s = params.serializerScheme.getSerializer(valueBinding);
734 for (int i=0; i<size; i++) {
735 array[i] = s.deserialize(b);
737 } catch (IOException e) {
738 throw new AccessorException( e );
739 } catch (SerializerConstructionException e) {
740 throw new AccessorException( e );
747 public void getAll(Binding valueBinding, Collection<Object> values)
748 throws AccessorException {
754 Serializer s = params.serializerScheme.getSerializer(valueBinding);
755 for (int i=0; i<size; i++) {
756 values.add( s.deserialize(b) );
758 } catch (IOException e) {
759 throw new AccessorException( e );
760 } catch (SerializerConstructionException e) {
761 throw new AccessorException( e );
768 public void setSizeNoflush(int newSize) throws AccessorException {
772 int oldSize = _size();
775 if (newSize<oldSize) {
776 remove(newSize, oldSize-newSize);
779 // Add dummy instances
780 if (newSize>oldSize) {
781 Object dummy = cb.createDefault();
782 int count = newSize-oldSize;
783 addRepeatNoflush(oldSize, cb, dummy, count);
786 } catch (BindingException e) {
787 throw new AccessorException( e );
794 public void setSize(int newSize) throws AccessorException {
798 setSizeNoflush(newSize);
800 } catch (IOException e) {
801 throw new AccessorException( e );
808 public int size() throws AccessorException {
812 return (int) ( b.length() / elementSize );
813 } catch (IOException e) {
814 throw new AccessorException( e );
820 private int _size() throws AccessorException {
822 return (int) ( b.length() / elementSize );
823 } catch (IOException e) {
824 throw new AccessorException( e );
829 Event applyLocal(Event e, boolean makeRollback) throws AccessorException {
830 Event rollback = null;
831 if (e instanceof ValueAssigned) {
832 ValueAssigned va = (ValueAssigned) e;
833 if (makeRollback) rollback = new ValueAssigned(cb, getValue(cb));
834 setValueNoflush(va.newValue.getBinding(), va.newValue.getValue());
836 if (e instanceof ArrayElementAdded) {
837 ArrayElementAdded aa = (ArrayElementAdded) e;
838 addNoflush(aa.index, aa.value.getBinding(), aa.value.getValue());
839 if (makeRollback) rollback = new ArrayElementRemoved(aa.index);
840 } else if (e instanceof ArrayElementRemoved) {
841 ArrayElementRemoved ar = (ArrayElementRemoved) e;
842 if (ar.index<0 || ar.index >=size()) throw new AccessorException("Array index out of bounds");
844 Object cv = get(ar.index, cb);
845 rollback = new ArrayElementAdded(ar.index, new MutableVariant(cb, cv));
847 removeNoflush(ar.index, 1);
849 throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Array");
858 public void add(Binding binding, Object value) throws AccessorException {
862 addNoflush(binding, value);
864 } catch (IOException e) {
865 throw new AccessorException( e );
872 public void add(int index, Binding binding, Object value)
873 throws AccessorException {
877 addNoflush(index, binding, value);
879 } catch (IOException e) {
880 throw new AccessorException( e );
887 public void addAll(Binding binding, Object[] values)
888 throws AccessorException {
892 addAllNoflush(binding, values);
894 } catch (IOException e) {
895 throw new AccessorException( e );
902 public void addAll(int index, Binding binding, Object[] values)
903 throws AccessorException {
907 addAllNoflush(index, binding, values);
909 } catch (IOException e) {
910 throw new AccessorException( e );
917 public void remove(int index, int count) throws AccessorException {
921 removeNoflush(index, count);
923 } catch (IOException e) {
924 throw new AccessorException( e );
931 public void set(int index, Binding binding, Object value)
932 throws AccessorException {
936 setNoflush(index, binding, value);
938 } catch (IOException e) {
939 throw new AccessorException(e);