]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryStreamArray.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / binary / BinaryStreamArray.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2011 Association for Decentralized Information Management in\r
3  * Industry THTH ry.\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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.databoard.accessor.binary;\r
13 \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
22 \r
23 import org.simantics.databoard.accessor.Accessor;\r
24 import org.simantics.databoard.accessor.ArrayAccessor;\r
25 import org.simantics.databoard.accessor.CloseableAccessor;\r
26 import org.simantics.databoard.accessor.StreamAccessor;\r
27 import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
28 import org.simantics.databoard.accessor.error.AccessorException;\r
29 import org.simantics.databoard.accessor.error.ReferenceException;\r
30 import org.simantics.databoard.accessor.event.ArrayElementAdded;\r
31 import org.simantics.databoard.accessor.event.ArrayElementRemoved;\r
32 import org.simantics.databoard.accessor.event.Event;\r
33 import org.simantics.databoard.accessor.event.ValueAssigned;\r
34 import org.simantics.databoard.accessor.file.FileArrayAccessor;\r
35 import org.simantics.databoard.accessor.impl.AccessorParams;\r
36 import org.simantics.databoard.accessor.impl.ListenerEntry;\r
37 import org.simantics.databoard.accessor.interestset.ArrayInterestSet;\r
38 import org.simantics.databoard.accessor.interestset.InterestSet;\r
39 import org.simantics.databoard.accessor.reference.ChildReference;\r
40 import org.simantics.databoard.accessor.reference.IndexReference;\r
41 import org.simantics.databoard.accessor.reference.LabelReference;\r
42 import org.simantics.databoard.adapter.AdaptException;\r
43 import org.simantics.databoard.binding.ArrayBinding;\r
44 import org.simantics.databoard.binding.Binding;\r
45 import org.simantics.databoard.binding.error.BindingException;\r
46 import org.simantics.databoard.binding.mutable.MutableVariant;\r
47 import org.simantics.databoard.serialization.Serializer;\r
48 import org.simantics.databoard.serialization.SerializerConstructionException;\r
49 import org.simantics.databoard.type.ArrayType;\r
50 import org.simantics.databoard.type.Datatype;\r
51 import org.simantics.databoard.util.binary.Blob;\r
52 import org.simantics.databoard.util.binary.RandomAccessBinary.ByteSide;\r
53 \r
54 /**\r
55  * Stream Array is accessor to an array where values can be added to\r
56  * the end, and where element size is constant. \r
57  * \r
58  * <p>\r
59  * The Binary format is different from the normal array format, there is no size\r
60  * integer at the beginning of the binary data. Instead the size is derieved\r
61  * from the size of the binary data by dividing byte size with element size.  \r
62  * Therefore, the element size must be constant.\r
63  *\r
64  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
65  */\r
66 public class BinaryStreamArray extends BinaryObject implements ArrayAccessor, FileArrayAccessor, CloseableAccessor, ArrayAccessor.CloseableArrayAccessor, StreamAccessor {\r
67 \r
68         /** Accessors to children */\r
69         TreeMap<Integer, java.lang.ref.Reference<BinaryObject>> children = new TreeMap<Integer, java.lang.ref.Reference<BinaryObject>>(); \r
70 \r
71         /** Element Binding */\r
72         Binding cb;\r
73         /** Element Serializer */\r
74         Serializer cs;\r
75         /** Element size */\r
76         int elementSize;\r
77         \r
78         public BinaryStreamArray(BinaryObject parent, Blob blob, Datatype type, AccessorParams params)\r
79         throws AccessorException\r
80         {\r
81                 super(parent, blob, type, params);\r
82                 ArrayType at = (ArrayType) type;                \r
83                 cb = params.bindingScheme.getBindingUnchecked(at.componentType);\r
84                 cs = params.serializerScheme.getSerializerUnchecked( cb );\r
85                 Integer elementConstantSize = cs.getConstantSize();\r
86                 if (elementConstantSize == null) {\r
87                         throw new AccessorException("The size in an element of an AppendableArray must be constant.");\r
88                 }\r
89                 elementSize = elementConstantSize;\r
90         }\r
91         \r
92         public ArrayType type() {\r
93                 return (ArrayType) type;\r
94         }       \r
95         \r
96         \r
97         /**\r
98          * Get existing sub accessor\r
99          * @param index\r
100          * @return sub-accessor or <code>null</code>\r
101          */\r
102         BinaryObject getExistingAccessor(int index)\r
103         {               \r
104                 java.lang.ref.Reference<BinaryObject> ref = children.get(index);\r
105                 if (ref==null) return null;\r
106                 BinaryObject res = (BinaryObject) ref.get();\r
107 //              if (res==null) children.remove(index);\r
108                 return res;\r
109         }       \r
110         \r
111         /**\r
112          * Get start position of a field\r
113          * \r
114          * @param fieldIndex\r
115          * @return\r
116          * @throws AccessorException\r
117          */\r
118         long getStartPosition(int fieldIndex) throws AccessorException {\r
119                 return fieldIndex * (long) (elementSize);\r
120         }       \r
121         \r
122         long getLength(int index, long pos) throws AccessorException {\r
123                 return elementSize;\r
124         }\r
125 \r
126         @Override\r
127         public void setNoflush(int index, Binding rcb, Object rcv)\r
128                         throws AccessorException {\r
129                 assert b.isOpen();\r
130                 writeLock();\r
131                 try {\r
132                         \r
133                         // Write                        \r
134                         Serializer rcs = params.serializerScheme.getSerializer( rcb );\r
135                         long pos = getStartPosition(index);\r
136                         b.position(pos);\r
137                         rcs.serialize(b, null, rcv);\r
138                                                 \r
139                         // Notify\r
140                         ListenerEntry le = listeners;\r
141                         while (le!=null) {                              \r
142                                 ArrayInterestSet is = le.getInterestSet();\r
143                                 if (is.inNotificationsOf(index)) {\r
144                                         MutableVariant newValue = null;\r
145                                         if (is.inValues()) newValue = new MutableVariant(rcb, rcb.isImmutable() ? rcv : rcb.clone(rcv));                                        \r
146                                         \r
147                                         Event e = new ValueAssigned(new IndexReference(index), newValue);\r
148                                         emitEvent(le, e);\r
149                                 }\r
150                                 le = le.next;\r
151                         }\r
152                         \r
153                 } catch (IOException e) {\r
154                         throw new AccessorException(e);\r
155                 } catch (AdaptException e) {\r
156                         throw new AccessorException(e);\r
157                 } catch (SerializerConstructionException e) {\r
158                         throw new AccessorException(e);\r
159                 } finally {\r
160                         writeUnlock();\r
161                 }\r
162                                 \r
163         }\r
164                 \r
165         /**\r
166          * Set all values\r
167          * \r
168          * @param arrayBinding\r
169          * @param newArray\r
170          */\r
171         @Override\r
172         public void setValueNoflush(Binding arrayBinding, Object newArray)\r
173                         throws AccessorException {\r
174                 assert b.isOpen();\r
175                 writeLock();\r
176                 try {\r
177                         // Write                        \r
178                         ArrayBinding rb = ((ArrayBinding)arrayBinding);\r
179                         Binding rcb = rb.getComponentBinding();\r
180                         Serializer rcs = params.serializerScheme.getSerializer( rcb );\r
181                         int oldCount = _size();\r
182                         int newCount = rb.size(newArray);\r
183                         b.setLength( newCount * elementSize );\r
184                         \r
185                         // Serialize\r
186                         b.position(0L);\r
187                         for (int index=0; index<newCount; index++) {\r
188                                 Object obj = rb.get(newArray, index);\r
189                                 rcs.serialize(b, obj);\r
190                         }\r
191                         \r
192                         // Notify removal\r
193                         for (int index=oldCount-1; index>=newCount; index--) {\r
194                                 BinaryObject sa = getExistingAccessor(index);\r
195                                 if (sa!=null) {\r
196                                         sa.invalidatedNotification();\r
197                                         children.remove(index);\r
198                                         sa = null;\r
199                                 }\r
200                         \r
201                                 // Notify changes\r
202                                 ListenerEntry le = listeners;\r
203                                 while (le!=null) {                              \r
204                                         ArrayInterestSet is = le.getInterestSet();\r
205                                         if (is.inNotificationsOf(index)) {\r
206                                                 Event e = new ArrayElementRemoved(index);\r
207                                                 emitEvent(le, e);\r
208                                         }\r
209                                         le = le.next;\r
210                                 }\r
211                         }\r
212                         \r
213                         // Notify new assignment\r
214                         if (listeners!=null) {\r
215                                 for (int index=0; index<newCount; index++) {\r
216                                         Object cv = rb.get(newArray, index);\r
217                                         \r
218                                         // Notify changes\r
219                                         ListenerEntry le = listeners;\r
220                                         while (le!=null) {                              \r
221                                                 ArrayInterestSet is = le.getInterestSet();\r
222                                                 if (is.inNotificationsOf(index)) {                                      \r
223                                                         MutableVariant vv = null;\r
224                                                         if (is.inValues()) vv = new MutableVariant(rcb, cb.isImmutable() ? cv : rcb.clone(cv));                                 \r
225                                                         \r
226                                                         Event e = index<oldCount ? new ValueAssigned(new IndexReference(index), vv) : new ArrayElementAdded(index, vv);\r
227                                                         emitEvent(le, e);\r
228                                                 }\r
229                                                 le = le.next;\r
230                                         }                               \r
231                                 }\r
232                         }\r
233                         \r
234                 } catch (BindingException e) {\r
235                         throw new AccessorException(e);\r
236                 } catch (IOException e) {\r
237                         throw new AccessorException(e);\r
238                 } catch (AdaptException e) {\r
239                         throw new AccessorException(e);\r
240                 } catch (SerializerConstructionException e) {\r
241                         throw new AccessorException(e);\r
242                 } finally {\r
243                         writeUnlock();                  \r
244                 }\r
245                 \r
246         }\r
247 \r
248         @Override\r
249         public void addNoflush(int index, Binding rcb, Object rcv) throws AccessorException {\r
250                 assert b.isOpen();\r
251                 writeLock();\r
252                 try {\r
253                         Serializer rcs = params.serializerScheme.getSerializer( rcb );                  \r
254                         // Write                \r
255                         int oldCount = _size();\r
256                         boolean lastEntry = index == oldCount;\r
257                         if (index>oldCount) throw new AccessorException("Index out of range");\r
258                         \r
259                         long pos = getStartPosition(index);\r
260                         b.position(pos);\r
261                         b.insertBytes(elementSize, ByteSide.Left);\r
262                         rcs.serialize(b, null, rcv);\r
263                         \r
264                         //  Update child map keys\r
265                         if (!lastEntry && !children.isEmpty()) {\r
266                                 Integer key = children.lastKey();\r
267                                 while (key != null && key >= index) {\r
268                                         java.lang.ref.Reference<BinaryObject> value = children.remove(key);\r
269                                         if (value.get()!=null) children.put(key+1, value);\r
270                                         key = children.lowerKey(key);\r
271                                 }\r
272                         }\r
273                                                 \r
274                         // Notify Listeners\r
275                         ListenerEntry le = listeners;\r
276                         while (le!=null) {                              \r
277                                 ArrayInterestSet is = le.getInterestSet();\r
278                                 if (is.inNotifications()) {\r
279                                         MutableVariant newValue = null;\r
280                                         if (is.inValues()) newValue = new MutableVariant(rcb, rcb.isImmutable() ? rcv : rcb.clone(rcv));                                        \r
281                                         ArrayElementAdded e = new ArrayElementAdded(index, newValue);\r
282                                         emitEvent(le, e);\r
283                                 }\r
284                                 \r
285                                 // Update indices of interest sets\r
286                                 if (is.componentInterests!=null) {\r
287                                         Map<Integer, InterestSet> oldCis = is.componentInterests;\r
288                                         boolean needUpdates = false;\r
289                                         for (Integer i : oldCis.keySet()) {\r
290                                                 needUpdates |= i>=index;\r
291                                                 if (needUpdates) break;\r
292                                         }\r
293                                         \r
294                                         if (needUpdates) {\r
295                                                 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size()); \r
296                                                 for (Integer i : oldCis.keySet())\r
297                                                 {\r
298                                                         Integer oldKey = i;\r
299                                                         Integer newKey = i>=index ? i+1 : i;\r
300                                                         InterestSet oldValue = oldCis.get(oldKey);\r
301                                                         newCis.put(newKey, oldValue); \r
302                                                 }\r
303                                                 is.componentInterests = newCis;\r
304                                         }\r
305                                 }\r
306                                                                         \r
307                                 le = le.next;\r
308                         }\r
309                         \r
310                 } catch (IOException e) {\r
311                         throw new AccessorException(e);\r
312                 } catch (AdaptException e) {\r
313                         throw new AccessorException(e);\r
314                 } catch (SerializerConstructionException e) {                   \r
315                         throw new AccessorException(e);\r
316                 } finally {\r
317                         writeUnlock();\r
318                 }\r
319         }\r
320 \r
321         @Override\r
322         public void addNoflush(Binding binding, Object value)\r
323                         throws AccessorException {\r
324                 addNoflush(_size(), binding, value);\r
325         }\r
326 \r
327         @Override\r
328         public void addAllNoflush(Binding binding, Object[] values)\r
329                         throws AccessorException {\r
330                 addAllNoflush(size(), binding, values);\r
331         }\r
332 \r
333         @Override\r
334         public void addAllNoflush(int index, Binding rcb, Object[] rcvs)\r
335                         throws AccessorException {\r
336                 if (index<0||index>size()) throw new AccessorException("Index out of bounds");\r
337                 assert b.isOpen();\r
338                 writeLock();\r
339                 try {\r
340                         Serializer rcs = params.serializerScheme.getSerializer( rcb );                  \r
341                         // Write                \r
342                         b.position(0L);\r
343                         int oldCount = b.readInt();\r
344                         int newCount = oldCount + rcvs.length;\r
345                         if (index>oldCount) throw new AccessorException("Index out of range");\r
346                         b.position(0L);\r
347                         b.writeInt(newCount);\r
348                         boolean lastEntry = index == oldCount;\r
349                         \r
350                         int size = 0;\r
351                         for (int i=0; i<rcvs.length; i++)\r
352                                 size += rcs.getSize(rcvs[i]);\r
353                         long pos = getStartPosition(index);\r
354                         b.position(pos);\r
355                         b.insertBytes(size, ByteSide.Right);\r
356                         \r
357                         b.position(pos);\r
358                         for (int i=0; i<rcvs.length; i++) {\r
359                                 Object rcv = rcvs[i];\r
360                                 rcs.serialize(b, rcv);                          \r
361                         }\r
362                         \r
363                         //  Update child map keys\r
364                         if (!lastEntry && !children.isEmpty()) {\r
365                                 Integer key = children.lastKey();\r
366                                 while (key!=null && key >= index) {\r
367                                         java.lang.ref.Reference<BinaryObject> value = children.remove(key);\r
368                                         if (value.get()!=null) children.put(key+rcvs.length, value);\r
369                                         key = children.lowerKey(key);\r
370                                 }\r
371                         }\r
372                                                 \r
373                         // Notify Listeners\r
374                         ListenerEntry le = listeners;\r
375                         while (le!=null) {                              \r
376                                 ArrayInterestSet is = le.getInterestSet();\r
377                                 if (is.inNotifications()) {\r
378                                         for (int i=0; i<rcvs.length; i++) {\r
379                                                 MutableVariant newValue = null;\r
380                                                 if (is.inValues()) newValue = new MutableVariant(rcb, rcb.isImmutable() ? rcvs[i] : cb.clone(rcvs[i]));                                 \r
381                                                 ArrayElementAdded e = new ArrayElementAdded(index, newValue);\r
382                                                 emitEvent(le, e);\r
383                                         }\r
384                                 }\r
385                                 \r
386                                 // Update indices of interest sets\r
387                                 if (is.componentInterests!=null) {\r
388                                         Map<Integer, InterestSet> oldCis = is.componentInterests;\r
389                                         boolean needUpdates = false;\r
390                                         for (Integer i : oldCis.keySet()) {\r
391                                                 needUpdates |= i>=index;\r
392                                                 if (needUpdates) break;\r
393                                         }\r
394                                         \r
395                                         if (needUpdates) {\r
396                                                 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size()); \r
397                                                 for (Integer i : oldCis.keySet())\r
398                                                 {\r
399                                                         Integer oldKey = i;\r
400                                                         Integer newKey = i>=index ? i+rcvs.length : i;\r
401                                                         InterestSet oldValue = oldCis.get(oldKey);\r
402                                                         newCis.put(newKey, oldValue); \r
403                                                 }\r
404                                                 is.componentInterests = newCis;\r
405                                         }\r
406                                         \r
407                                         // Add component interest listener\r
408                                         /*\r
409                                         for (int i = index; i<index+rcvs.length; i++) {\r
410                                                 boolean hadSa = getExistingAccessor(i)!=null;\r
411                                                 if (hadSa) continue;\r
412                                                  \r
413                                                 InterestSet cis = is.getComponentInterest(); \r
414                                                 if (cis != null) {\r
415                                                         Accessor sa = getAccessor(i);\r
416                                                 }                               \r
417                                                 cis = is.getComponentInterest(index); \r
418                                                 if (cis != null) {\r
419                                                         Accessor sa = getAccessor(i);\r
420                                                 }\r
421                                         }\r
422                                         */\r
423                                         \r
424                                 }\r
425                                 \r
426                                 le = le.next;\r
427                         }\r
428                         \r
429                 } catch (IOException e) {\r
430                         throw new AccessorException(e);\r
431                 } catch (AdaptException e) {\r
432                         throw new AccessorException(e);\r
433                 } catch (SerializerConstructionException e) {\r
434                         throw new AccessorException(e);\r
435                 } finally {\r
436                         writeUnlock();\r
437                 }\r
438         }\r
439 \r
440         void addRepeatNoflush(int index, Binding rcb, Object obj, int repeatCount) throws AccessorException {\r
441                 if (index<0||index>size()) throw new AccessorException("Index out of bounds");\r
442                 assert b.isOpen();\r
443                 writeLock();\r
444                 try {\r
445                         Serializer rcs = params.serializerScheme.getSerializer( rcb );                  \r
446                         // Write                \r
447                         int oldCount = _size();\r
448                         int newCount = oldCount + repeatCount;\r
449                         if (index>oldCount) throw new AccessorException("Index out of range");\r
450                         b.position(0L);\r
451                         b.writeInt(newCount);\r
452                         boolean lastEntry = index == oldCount;\r
453                         \r
454                         int size = rcs.getSize(obj) * repeatCount;\r
455                         long pos = getStartPosition(index);\r
456                         b.position(pos);\r
457                         b.insertBytes(size, ByteSide.Right);\r
458                         \r
459                         b.position(pos);\r
460                         for (int i=0; i<repeatCount; i++) {\r
461                                 rcs.serialize(b, obj);                          \r
462                         }\r
463                         \r
464                         //  Update child map keys\r
465                         if (!lastEntry && !children.isEmpty()) {\r
466                                 Integer key = children.lastKey();\r
467                                 while (key!=null && key >= index) {\r
468                                         java.lang.ref.Reference<BinaryObject> value = children.remove(key);\r
469                                         if (value.get()!=null) children.put(key+repeatCount, value);\r
470                                         key = children.lowerKey(key);\r
471                                 }\r
472                         }\r
473                                                 \r
474                         // Notify Listeners\r
475                         ListenerEntry le = listeners;\r
476                         while (le!=null) {                              \r
477                                 ArrayInterestSet is = le.getInterestSet();\r
478                                 if (is.inNotifications()) {\r
479                                         for (int i=0; i<repeatCount; i++) {\r
480                                                 MutableVariant newValue = null;\r
481                                                 if (is.inValues()) newValue = new MutableVariant(rcb, obj/*rcb.isImmutable() ? obj : cb.clone(obj)*/);                                  \r
482                                                 ArrayElementAdded e = new ArrayElementAdded(index, newValue);\r
483                                                 emitEvent(le, e);\r
484                                         }\r
485                                 }\r
486                                 \r
487                                 // Update indices of interest sets\r
488                                 if (is.componentInterests!=null) {\r
489                                         Map<Integer, InterestSet> oldCis = is.componentInterests;\r
490                                         boolean needUpdates = false;\r
491                                         for (Integer i : oldCis.keySet()) {\r
492                                                 needUpdates |= i>=index;\r
493                                                 if (needUpdates) break;\r
494                                         }\r
495                                         \r
496                                         if (needUpdates) {\r
497                                                 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size()); \r
498                                                 for (Integer i : oldCis.keySet())\r
499                                                 {\r
500                                                         Integer oldKey = i;\r
501                                                         Integer newKey = i>=index ? i+repeatCount : i;\r
502                                                         InterestSet oldValue = oldCis.get(oldKey);\r
503                                                         newCis.put(newKey, oldValue); \r
504                                                 }\r
505                                                 is.componentInterests = newCis;\r
506                                         }\r
507                                         \r
508                                 }\r
509                                 \r
510                                 le = le.next;\r
511                         }\r
512                         \r
513                 } catch (IOException e) {\r
514                         throw new AccessorException(e);\r
515                 } catch (SerializerConstructionException e) {\r
516                         throw new AccessorException(e);\r
517                 } finally {\r
518                         writeUnlock();\r
519                 }\r
520         }\r
521         \r
522         @Override\r
523         public void removeNoflush(int index, int count) throws AccessorException {\r
524                 assert b.isOpen();\r
525                 writeLock();\r
526                 try {\r
527                         // Write\r
528                         boolean lastEntry = index == count;\r
529                         int oldCount = _size();\r
530                         if (index<0||index+count>oldCount) throw new AccessorException("Index out of bounds");\r
531                         long pos = getStartPosition(index);                     \r
532                         long lastPos = getStartPosition(index+count-1);                 \r
533                         long lastLen = getLength(index, lastPos);\r
534                         long end = lastPos + lastLen;\r
535                         long len = end - pos;\r
536                         b.position(pos);\r
537                         b.removeBytes(len, ByteSide.Right);\r
538                         \r
539                         // Remove children\r
540                         SortedMap<Integer, java.lang.ref.Reference<BinaryObject>> sm = children.subMap(index, true, index+count, false);\r
541                         for (Entry<Integer, java.lang.ref.Reference<BinaryObject>> e : sm.entrySet()) {\r
542                             BinaryObject bo = e.getValue().get();\r
543                             if (bo==null) continue;\r
544                             bo.invalidatedNotification();                           \r
545                         }\r
546                         sm.clear();\r
547                         \r
548                         //  Update the keys of consecutive children\r
549                         if (!lastEntry && !children.isEmpty()) {\r
550                                 Integer lastKey = children.lastKey();\r
551                                 Integer key = children.higherKey(index);\r
552                                 while (key != null && key <= lastKey) {\r
553                                         java.lang.ref.Reference<BinaryObject> value = children.remove(key);\r
554                                         if (value.get()!=null) children.put(key-count, value);\r
555                                         key = children.higherKey(key);\r
556                                 }\r
557                         }\r
558                                                 \r
559                         // Notify Listeners\r
560                         ListenerEntry le = listeners;\r
561                         while (le!=null) {                              \r
562                                 ArrayInterestSet is = le.getInterestSet();\r
563                                 if (is.inNotifications()) {\r
564                                         ArrayElementRemoved e = new ArrayElementRemoved(index);\r
565                                         emitEvent(le, e);\r
566                                 }\r
567                                 \r
568                                 // Update indices of interest sets\r
569                                 if (is.componentInterests!=null) {\r
570                                         Map<Integer, InterestSet> oldCis = is.componentInterests;\r
571                                         boolean needUpdates = false;\r
572                                         for (Integer i : oldCis.keySet()) {\r
573                                                 needUpdates |= i>=index;\r
574                                                 if (needUpdates) break;\r
575                                         }\r
576                                         \r
577                                         if (needUpdates) {\r
578                                                 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size()); \r
579                                                 for (Integer i : oldCis.keySet())\r
580                                                 {\r
581                                                         Integer oldKey = i;\r
582                                                         Integer newKey = i>=index ? i-1 : i;\r
583                                                         InterestSet oldValue = oldCis.get(oldKey);\r
584                                                         newCis.put(newKey, oldValue); \r
585                                                 }\r
586                                                 is.componentInterests = newCis;\r
587                                         }\r
588                                 }                               \r
589                                 le = le.next;\r
590                         }\r
591                         \r
592                         \r
593                 } catch (IOException e) {\r
594                         throw new AccessorException( e );\r
595                 } finally {\r
596                         writeUnlock();\r
597                 }\r
598         }\r
599         \r
600         @Override\r
601         public Object get(int index, Binding valueBinding) throws AccessorException {\r
602                 assert b.isOpen();\r
603                 readLock();\r
604                 try {\r
605                         long pos = getStartPosition(index);\r
606                         b.position(pos);\r
607                         Serializer s = params.serializerScheme.getSerializer(valueBinding);\r
608                         return s.deserialize(b);\r
609                 } catch (IOException e) {\r
610                         throw new AccessorException(e);\r
611                 } catch (SerializerConstructionException e) {\r
612                         throw new AccessorException(e);\r
613                 } finally {\r
614                         readUnlock();\r
615                 }\r
616         }\r
617 \r
618         @Override\r
619         public void get(int index, Binding valueBinding, Object dst) throws AccessorException {\r
620                 assert b.isOpen();\r
621                 readLock();\r
622                 try {\r
623                         long pos = getStartPosition(index);\r
624                         b.position(pos);\r
625                         Serializer s = params.serializerScheme.getSerializer(valueBinding);\r
626                         s.deserializeTo(b, dst);\r
627                 } catch (IOException e) {\r
628                         throw new AccessorException(e);\r
629                 } catch (SerializerConstructionException e) {\r
630                         throw new AccessorException(e);\r
631                 } finally {\r
632                         readUnlock();\r
633                 }\r
634         }\r
635 \r
636         \r
637         @SuppressWarnings("unchecked")\r
638         @Override\r
639         public <T extends Accessor> T getAccessor(int index)\r
640                         throws AccessorConstructionException {\r
641                 assert b.isOpen();\r
642                 readLock();\r
643                 try {\r
644                         int count = _size();\r
645                         if (index<0 || index>=count) throw new ReferenceException("Element index ("+index+") out of bounds ("+count+")");\r
646                         \r
647                         // Get existing or create new\r
648                         BinaryObject sa = getExistingAccessor(index);\r
649                         if (sa==null) {\r
650                                 long pos = getStartPosition(index);\r
651                                 long len = getLength(index, pos);\r
652 \r
653                                 // Instantiate correct sub accessor. \r
654                                 sa = createSubAccessor(cb.type(), pos, len, params);                            \r
655                                 children.put(index, new WeakReference<BinaryObject>(sa) );\r
656 \r
657                                 // Add component interest sets\r
658                                 ListenerEntry le = listeners;\r
659                                 while (le!=null) {                              \r
660                                         ArrayInterestSet is = le.getInterestSet();\r
661 \r
662                                         // Generic element interest\r
663                                         InterestSet gis = is.getComponentInterest(); \r
664                                         if (gis != null) {\r
665                                                 try {\r
666                                                         ChildReference childPath = ChildReference.concatenate(le.path, new IndexReference(index) );\r
667                                                         sa.addListener(le.listener, gis, childPath, le.executor);\r
668                                                 } catch (AccessorException e) {\r
669                                                         throw new AccessorConstructionException(e);\r
670                                                 }\r
671                                         }\r
672                                         \r
673                                         // Specific element interest\r
674                                         InterestSet cis = is.getComponentInterest(index); \r
675                                         if (cis != null) {\r
676                                                 try {\r
677                                                         ChildReference childPath = ChildReference.concatenate(le.path, new IndexReference(index) );\r
678                                                         sa.addListener(le.listener, cis, childPath,le.executor);\r
679                                                 } catch (AccessorException e) {\r
680                                                         throw new AccessorConstructionException(e);\r
681                                                 }\r
682                                         }\r
683                                         \r
684                                         // Next listener\r
685                                         le = le.next;\r
686                                 }                               \r
687                                 \r
688                         }\r
689                         \r
690                         return (T) sa;\r
691                 } catch (AccessorException e) {\r
692                         throw new AccessorConstructionException(e);\r
693                 } finally {\r
694                         readUnlock();\r
695                 }\r
696         }\r
697 \r
698         @SuppressWarnings("unchecked")\r
699         @Override\r
700         public <T extends Accessor> T getComponent(ChildReference reference)\r
701                         throws AccessorConstructionException {\r
702                 if (reference==null) return (T) this;\r
703                 if (reference instanceof LabelReference) {\r
704                         LabelReference lr = (LabelReference) reference;\r
705                         try {\r
706                                 Integer index = new Integer( lr.label );\r
707                                 Accessor result = getAccessor(index);\r
708                                 if (reference.getChildReference() != null)\r
709                                         result = result.getComponent(reference.getChildReference());\r
710                                 return (T) result;\r
711                         } catch ( NumberFormatException nfe ) {\r
712                                 throw new ReferenceException(nfe);\r
713                         }                       \r
714                 } else if (reference instanceof IndexReference) {\r
715                         IndexReference ref = (IndexReference) reference;\r
716                         int index = ref.getIndex();\r
717                         Accessor result = getAccessor(index);\r
718                         if (reference.getChildReference() != null)\r
719                                 result = result.getComponent(reference.getChildReference());\r
720                         return (T) result;\r
721                 } throw new ReferenceException(reference.getClass().getName()+" is not a reference of an array");       \r
722         }\r
723         \r
724         @Override\r
725         public void getAll(Binding valueBinding, Object[] array)\r
726                         throws AccessorException {\r
727                 assert b.isOpen();\r
728                 readLock();\r
729                 try {\r
730                         int size = _size();\r
731                         if (size > array.length) throw new AccessorException("Argument array too short");\r
732                         Serializer s = params.serializerScheme.getSerializer(valueBinding);\r
733                         b.position(0L);\r
734                         for (int i=0; i<size; i++) {\r
735                                 array[i] = s.deserialize(b);\r
736                         }\r
737                 } catch (IOException e) {\r
738                         throw new AccessorException( e );\r
739                 } catch (SerializerConstructionException e) {\r
740                         throw new AccessorException( e );\r
741                 } finally {\r
742                         readUnlock();\r
743                 }\r
744         }\r
745 \r
746         @Override\r
747         public void getAll(Binding valueBinding, Collection<Object> values)\r
748                         throws AccessorException {\r
749                 assert b.isOpen();\r
750                 readLock();\r
751                 try {\r
752                         b.position(0L);\r
753                         int size = _size();\r
754                         Serializer s = params.serializerScheme.getSerializer(valueBinding);\r
755                         for (int i=0; i<size; i++) {\r
756                                 values.add( s.deserialize(b) );\r
757                         }\r
758                 } catch (IOException e) {\r
759                         throw new AccessorException( e );\r
760                 } catch (SerializerConstructionException e) {\r
761                         throw new AccessorException( e );\r
762                 } finally {\r
763                         readUnlock();\r
764                 }\r
765         }\r
766         \r
767         @Override\r
768         public void setSizeNoflush(int newSize) throws AccessorException {\r
769                 assert b.isOpen();\r
770                 writeLock();\r
771                 try {\r
772                         int oldSize = _size();\r
773 \r
774                         // Remove instances  \r
775                         if (newSize<oldSize) {\r
776                                 remove(newSize, oldSize-newSize);\r
777                         }\r
778                                                 \r
779                         // Add dummy instances\r
780                         if (newSize>oldSize) {\r
781                                 Object dummy = cb.createDefault();\r
782                                 int count = newSize-oldSize;\r
783                                 addRepeatNoflush(oldSize, cb, dummy, count);\r
784                         }\r
785                         \r
786                 } catch (BindingException e) {\r
787                         throw new AccessorException( e );\r
788                 } finally {\r
789                         writeUnlock();\r
790                 }\r
791         }\r
792 \r
793         @Override\r
794         public void setSize(int newSize) throws AccessorException {\r
795                 assert b.isOpen();\r
796                 writeLock();\r
797                 try {\r
798                         setSizeNoflush(newSize);\r
799                         b.flush();\r
800                 } catch (IOException e) {\r
801                         throw new AccessorException( e );\r
802                 } finally {\r
803                         writeUnlock();\r
804                 }\r
805         }\r
806         \r
807         @Override\r
808         public int size() throws AccessorException {\r
809                 assert b.isOpen();\r
810                 readLock();\r
811                 try {\r
812                         return (int) ( b.length() / elementSize );\r
813                 } catch (IOException e) {\r
814                         throw new AccessorException( e );\r
815                 } finally {\r
816                         readUnlock();\r
817                 }\r
818         }\r
819         \r
820         private int _size() throws AccessorException {\r
821                 try {\r
822                         return (int) ( b.length() / elementSize );\r
823                 } catch (IOException e) {\r
824                         throw new AccessorException( e );\r
825                 }\r
826         }\r
827 \r
828         @Override\r
829         Event applyLocal(Event e, boolean makeRollback) throws AccessorException {\r
830                 Event rollback = null;\r
831                 if (e instanceof ValueAssigned) {\r
832                         ValueAssigned va = (ValueAssigned) e;\r
833                         if (makeRollback) rollback = new ValueAssigned(cb, getValue(cb)); \r
834                         setValueNoflush(va.newValue.getBinding(), va.newValue.getValue());\r
835                 } else          \r
836                 if (e instanceof ArrayElementAdded) {\r
837                         ArrayElementAdded aa = (ArrayElementAdded) e;\r
838                         addNoflush(aa.index, aa.value.getBinding(), aa.value.getValue());\r
839                         if (makeRollback) rollback = new ArrayElementRemoved(aa.index);\r
840                 } else if (e instanceof ArrayElementRemoved) {\r
841                         ArrayElementRemoved ar = (ArrayElementRemoved) e;\r
842                         if (ar.index<0 || ar.index >=size()) throw new AccessorException("Array index out of bounds");\r
843                         if (makeRollback) {\r
844                                 Object cv = get(ar.index, cb);\r
845                                 rollback = new ArrayElementAdded(ar.index, new MutableVariant(cb, cv));\r
846                         }\r
847                         removeNoflush(ar.index, 1);\r
848                 } else {\r
849                         throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Array");\r
850                 }\r
851                 \r
852                 return rollback;\r
853         }\r
854 \r
855         \r
856         \r
857         @Override\r
858         public void add(Binding binding, Object value) throws AccessorException {\r
859                 assert b.isOpen();\r
860                 writeLock();\r
861                 try {\r
862                         addNoflush(binding, value);\r
863                         b.flush();\r
864                 } catch (IOException e) {\r
865                         throw new AccessorException( e );\r
866                 } finally {\r
867                         writeUnlock();\r
868                 }\r
869         }\r
870 \r
871         @Override\r
872         public void add(int index, Binding binding, Object value)\r
873                         throws AccessorException {\r
874                 assert b.isOpen();\r
875                 writeLock();\r
876                 try {\r
877                         addNoflush(index, binding, value);\r
878                         b.flush();\r
879                 } catch (IOException e) {\r
880                         throw new AccessorException( e );\r
881                 } finally {\r
882                         writeUnlock();\r
883                 }\r
884         }\r
885 \r
886         @Override\r
887         public void addAll(Binding binding, Object[] values)\r
888                         throws AccessorException {\r
889                 assert b.isOpen();\r
890                 writeLock();\r
891                 try {\r
892                         addAllNoflush(binding, values);\r
893                         b.flush();\r
894                 } catch (IOException e) {\r
895                         throw new AccessorException( e );\r
896                 } finally {\r
897                         writeUnlock();\r
898                 }\r
899         }\r
900 \r
901         @Override\r
902         public void addAll(int index, Binding binding, Object[] values)\r
903                         throws AccessorException {\r
904                 assert b.isOpen();\r
905                 writeLock();\r
906                 try {\r
907                         addAllNoflush(index, binding, values);\r
908                         b.flush();\r
909                 } catch (IOException e) {\r
910                         throw new AccessorException( e );\r
911                 } finally {\r
912                         writeUnlock();                  \r
913                 }\r
914         }\r
915 \r
916         @Override\r
917         public void remove(int index, int count) throws AccessorException {\r
918                 assert b.isOpen();\r
919                 writeLock();\r
920                 try {\r
921                         removeNoflush(index, count);\r
922                         b.flush();\r
923                 } catch (IOException e) {\r
924                         throw new AccessorException( e );\r
925                 } finally {\r
926                         writeUnlock();\r
927                 }\r
928         }\r
929 \r
930         @Override\r
931         public void set(int index, Binding binding, Object value)\r
932                         throws AccessorException {\r
933                 assert b.isOpen();\r
934                 writeLock();\r
935                 try {\r
936                         setNoflush(index, binding, value);\r
937                         b.flush();\r
938                 } catch (IOException e) {\r
939                         throw new AccessorException(e);\r
940                 } finally {\r
941                         writeUnlock();\r
942                 }\r
943         }\r
944         \r
945 }\r
946 \r