]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryVariableWidthStreamArray.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / binary / BinaryVariableWidthStreamArray.java
1 /*******************************************************************************\r
2  *  Copyright (c) 2010 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.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
55 \r
56 /**\r
57  * Binary Array is accessor to a byte backed array of elements.\r
58  * <p>\r
59  * \r
60  * Note, To increase the random access performance of the record, create sub-accessors of\r
61  * its fields. \r
62  *\r
63  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
64  */\r
65 public class BinaryVariableWidthStreamArray extends BinaryObject implements ArrayAccessor, FileArrayAccessor, ArrayAccessor.CloseableArrayAccessor, StreamAccessor {\r
66 \r
67         /** Accessors to children */\r
68         TreeMap<Integer, java.lang.ref.Reference<BinaryObject>> children = new TreeMap<Integer, java.lang.ref.Reference<BinaryObject>>(); \r
69 \r
70         Binding cb;\r
71         Serializer cs;\r
72         Integer constantSize;\r
73         ArrayAccessor index;\r
74         \r
75         public BinaryVariableWidthStreamArray(BinaryObject parent, Blob blob, Datatype type, AccessorParams params, ArrayAccessor index) \r
76         throws AccessorConstructionException\r
77         {\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
85                 }\r
86                 this.index = index;\r
87         }\r
88         \r
89         public ArrayType type() {\r
90                 return (ArrayType) type;\r
91         }\r
92         \r
93         /**\r
94          * Get existing sub accessor\r
95          * @param index\r
96          * @return sub-accessor or <code>null</code>\r
97          */\r
98         BinaryObject getExistingAccessor(int index)\r
99         {               \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
104                 return res;\r
105         }       \r
106         \r
107         /**\r
108          * Get start position of a field\r
109          * \r
110          * @param fieldIndex\r
111          * @return\r
112          * @throws AccessorException\r
113          */\r
114         long getStartPosition(int fieldIndex) throws AccessorException {\r
115                 int c = this.index.size();\r
116                 if ( fieldIndex==c )\r
117                         try {\r
118                                 return b.length();\r
119                         } catch (IOException e) {\r
120                                 throw new AccessorException( e );\r
121                         }\r
122                 if ( fieldIndex>c || fieldIndex<0 ) throw new AccessorException("Index out of bounds");\r
123                 return (Long) index.get(fieldIndex, Bindings.LONG);\r
124         }       \r
125         \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
129                 if ( index==c-1 ) \r
130                 {\r
131                         try {\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
135                         }\r
136                 } else {\r
137                         return (Long) this.index.get(index+1, Bindings.LONG) - (Long) this.index.get(index, Bindings.LONG);\r
138                 }\r
139         }\r
140 \r
141         @Override\r
142         public void setNoflush(int index, Binding rcb, Object rcv)\r
143                         throws AccessorException {\r
144                 assert b.isOpen();\r
145                 \r
146                 writeLock();\r
147                 try {\r
148 \r
149                         int count = size();\r
150                         if ( index==count ) {\r
151                                 addNoflush(count, rcb, rcv);\r
152                                 return;\r
153                         }                                       \r
154                         \r
155                         // Write                        \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
160                         b.position(pos);\r
161                         long diff = newSize - oldSize; \r
162                         if (diff>0) {\r
163                                 b.insertBytes( newSize - oldSize, ByteSide.Right );\r
164                         } else if (diff<0) { \r
165                             b.removeBytes( oldSize - newSize, ByteSide.Right );\r
166                         }\r
167                         b.position(pos);\r
168                         rcs.serialize(b, null, rcv);\r
169 \r
170                         // Update index of the higher indices\r
171                         if ( diff!= 0 ) {\r
172                                 for (int i=index+1; i<count; i++) {\r
173                                         long p = (Long) this.index.get(i, Bindings.LONG);\r
174                                         p += diff;\r
175                                         this.index.set(i, Bindings.LONG, p);\r
176                                 }\r
177                         }\r
178                         \r
179                         // Update Blobs\r
180                         /*\r
181                         BinaryObject sa = getExistingAccessor(index);\r
182                         if (sa!=null && newSize != oldSize) {\r
183                                 sa.b.setPositionInSource(pos, newSize);\r
184                         }*/\r
185                         \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
195                                         }\r
196                                 }\r
197                         }*/\r
198                         \r
199                         // Notify\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
206                                         \r
207                                         Event e = new ValueAssigned(new IndexReference(index), newValue);\r
208                                         emitEvent(le, e);\r
209                                 }\r
210                                 le = le.next;\r
211                         }\r
212                         \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
219                 } finally {\r
220                         writeUnlock();\r
221                 }\r
222                                 \r
223         }\r
224                 \r
225         /**\r
226          * Set all values\r
227          * \r
228          * @param arrayBinding\r
229          * @param newArray\r
230          */\r
231         @Override\r
232         public void setValueNoflush(Binding arrayBinding, Object newArray)\r
233                         throws AccessorException {\r
234                 assert b.isOpen();\r
235                 writeLock();\r
236                 try {\r
237                         // Write                        \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
245                         \r
246                         // Serialize\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
254                                 \r
255                                 // update location\r
256                                 /*\r
257                                 BinaryObject sa = getExistingAccessor(index);\r
258                                 if (sa!=null) {\r
259                                         sa.b.setPositionInSource(startPos, len);\r
260                                 }*/\r
261                         }\r
262                         \r
263                         // Notify removal\r
264                         for (int index=oldCount-1; index>=newCount; index--) {\r
265                                 BinaryObject sa = getExistingAccessor(index);\r
266                                 if (sa!=null) {\r
267                                         sa.invalidatedNotification();\r
268                                         children.remove(index);\r
269                                         sa = null;\r
270                                 }\r
271                         \r
272                                 // Notify changes\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
278                                                 emitEvent(le, e);\r
279                                         }\r
280                                         le = le.next;\r
281                                 }\r
282                         }\r
283                         \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
288                                         \r
289                                         // Notify changes\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
296                                                         \r
297                                                         Event e = index<oldCount ? new ValueAssigned(new IndexReference(index), vv) : new ArrayElementAdded(index, vv);\r
298                                                         emitEvent(le, e);\r
299                                                 }\r
300                                                 le = le.next;\r
301                                         }                               \r
302                                 }\r
303                         }\r
304                         \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
313                 } finally {\r
314                         writeUnlock();                  \r
315                 }\r
316                 \r
317         }\r
318 \r
319         @Override\r
320         public void addNoflush(int index, Binding rcb, Object rcv) throws AccessorException {\r
321                 assert b.isOpen();\r
322                 writeLock();\r
323                 try {\r
324                         Serializer rcs = params.serializerScheme.getSerializer( rcb );                  \r
325                         // Write                \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
330                         \r
331                         long pos = getStartPosition(index);\r
332                         int size = rcs.getSize(rcv);\r
333                         b.position(pos);\r
334                         b.insertBytes(size, ByteSide.Left);\r
335                         rcs.serialize(b, null, rcv);\r
336                         \r
337                         this.index.add(index, Bindings.LONG, pos);\r
338                         \r
339                         for (int i=index+1; i<newCount; i++) {\r
340                                 long p = (Long) this.index.get(i, Bindings.LONG);\r
341                                 p += size;\r
342                                 this.index.set(i, Bindings.LONG, p);\r
343                         }\r
344                         \r
345                         \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
353                                 }\r
354                         }\r
355                                                 \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
364                                         emitEvent(le, e);\r
365                                 }\r
366                                 \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
374                                         }\r
375                                         \r
376                                         if (needUpdates) {\r
377                                                 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size()); \r
378                                                 for (Integer i : oldCis.keySet())\r
379                                                 {\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
384                                                 }\r
385                                                 is.componentInterests = newCis;\r
386                                         }\r
387                                 }\r
388                                 \r
389                                 // Add component interest listener\r
390                                 /*\r
391                                 boolean hadSa = getExistingAccessor(index) != null;                             \r
392                                 if (!hadSa) {\r
393                                         // Add component interest listener\r
394                                         InterestSet cis = is.getComponentInterest(); \r
395                                         if (cis != null) {\r
396                                                 Accessor sa = getAccessor(index);\r
397                                         }                               \r
398                                         cis = is.getComponentInterest(index); \r
399                                         if (cis != null) {\r
400                                                 Accessor sa = getAccessor(index);\r
401                                         }\r
402                                 }       */              \r
403                                 \r
404                                 le = le.next;\r
405                         }\r
406                         \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
413                 } finally {\r
414                         writeUnlock();\r
415                 }\r
416         }\r
417 \r
418         @Override\r
419         public void addNoflush(Binding binding, Object value)\r
420                         throws AccessorException {\r
421                 addNoflush(size(), binding, value);\r
422         }\r
423 \r
424         @Override\r
425         public void addAllNoflush(Binding binding, Object[] values)\r
426                         throws AccessorException {\r
427                 addAllNoflush(size(), binding, values);\r
428         }\r
429 \r
430         @Override\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
434                 assert b.isOpen();\r
435                 writeLock();\r
436                 try {\r
437                         Serializer rcs = params.serializerScheme.getSerializer( rcb );                  \r
438                         // Write                \r
439                         b.position(0L);\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
445                         \r
446                         long size = 0;\r
447                         for (int i=0; i<rcvs.length; i++)\r
448                                 size += rcs.getSize(rcvs[i]);\r
449                         long pos = getStartPosition(index);\r
450                         b.position(pos);\r
451                         b.insertBytes(size, ByteSide.Right);\r
452 \r
453                         long p = pos;\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
457                         }\r
458                         for (int i=index+repeatCount; i<newCount; i++) {\r
459                                 p = (Long) this.index.get(i, Bindings.LONG);\r
460                                 p += size;\r
461                                 this.index.set(i, Bindings.LONG, p);\r
462                         }\r
463                         \r
464                         b.position(pos);\r
465                         for (int i=0; i<rcvs.length; i++) {\r
466                                 Object rcv = rcvs[i];\r
467                                 rcs.serialize(b, rcv);                          \r
468                         }\r
469                         \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
477                                 }\r
478                         }\r
479                                                 \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
489                                                 emitEvent(le, e);\r
490                                         }\r
491                                 }\r
492                                 \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
500                                         }\r
501                                         \r
502                                         if (needUpdates) {\r
503                                                 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size()); \r
504                                                 for (Integer i : oldCis.keySet())\r
505                                                 {\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
510                                                 }\r
511                                                 is.componentInterests = newCis;\r
512                                         }\r
513                                         \r
514                                         // Add component interest listener\r
515                                         /*\r
516                                         for (int i = index; i<index+rcvs.length; i++) {\r
517                                                 boolean hadSa = getExistingAccessor(i)!=null;\r
518                                                 if (hadSa) continue;\r
519                                                  \r
520                                                 InterestSet cis = is.getComponentInterest(); \r
521                                                 if (cis != null) {\r
522                                                         Accessor sa = getAccessor(i);\r
523                                                 }                               \r
524                                                 cis = is.getComponentInterest(index); \r
525                                                 if (cis != null) {\r
526                                                         Accessor sa = getAccessor(i);\r
527                                                 }\r
528                                         }\r
529                                         */\r
530                                         \r
531                                 }\r
532                                 \r
533                                 le = le.next;\r
534                         }\r
535                         \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
542                 } finally {\r
543                         writeUnlock();\r
544                 }\r
545         }\r
546 \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
549                 assert b.isOpen();\r
550                 writeLock();\r
551                 try {\r
552                         Serializer rcs = params.serializerScheme.getSerializer( rcb );                  \r
553                         // Write                \r
554                         b.position(0L);\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
559                         \r
560                         long componentSize = rcs.getSize(obj);\r
561                         long size = componentSize * repeatCount;\r
562                         long pos = getStartPosition(index);\r
563                         b.position(pos);\r
564                         b.insertBytes(size, ByteSide.Right);\r
565                         long p = pos;\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
569                         }\r
570                         for (int i=index+repeatCount; i<newCount; i++) {\r
571                                 p = (Long) this.index.get(i, Bindings.LONG);\r
572                                 p += size;\r
573                                 this.index.set(i, Bindings.LONG, p);\r
574                         }\r
575                         \r
576                         b.position(pos);\r
577                         for (int i=0; i<repeatCount; i++) {\r
578                                 rcs.serialize(b, obj);                          \r
579                         }\r
580                         \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
588                                 }\r
589                         }\r
590                                                 \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
600                                                 emitEvent(le, e);\r
601                                         }\r
602                                 }\r
603                                 \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
611                                         }\r
612                                         \r
613                                         if (needUpdates) {\r
614                                                 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size()); \r
615                                                 for (Integer i : oldCis.keySet())\r
616                                                 {\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
621                                                 }\r
622                                                 is.componentInterests = newCis;\r
623                                         }\r
624                                         \r
625                                 }\r
626                                 \r
627                                 le = le.next;\r
628                         }\r
629                         \r
630                 } catch (IOException e) {\r
631                         throw new AccessorException(e);\r
632                 } catch (SerializerConstructionException e) {\r
633                         throw new AccessorException(e);\r
634                 } finally {\r
635                         writeUnlock();\r
636                 }\r
637         }\r
638         \r
639         @Override\r
640         public void removeNoflush(int index, int count) throws AccessorException {\r
641                 assert b.isOpen();\r
642                 writeLock();\r
643                 try {\r
644                         // Write\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
654                         b.position(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
659                                 p -= len;\r
660                                 this.index.set(i, Bindings.LONG, p);\r
661                         }\r
662                         \r
663                         // Remove children\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
669                         }\r
670                         sm.clear();\r
671                         \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
680                                 }\r
681                         }\r
682                                                 \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
689                                         emitEvent(le, e);\r
690                                 }\r
691                                 \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
699                                         }\r
700                                         \r
701                                         if (needUpdates) {\r
702                                                 Map<Integer, InterestSet> newCis = new HashMap<Integer, InterestSet>(oldCis.size()); \r
703                                                 for (Integer i : oldCis.keySet())\r
704                                                 {\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
709                                                 }\r
710                                                 is.componentInterests = newCis;\r
711                                         }\r
712                                 }                               \r
713                                 le = le.next;\r
714                         }\r
715                         \r
716                         \r
717                 } catch (IOException e) {\r
718                         throw new AccessorException( e );\r
719                 } finally {\r
720                         writeUnlock();\r
721                 }\r
722         }\r
723         \r
724         @Override\r
725         public Object get(int index, Binding valueBinding) throws AccessorException {\r
726                 assert b.isOpen();\r
727                 readLock();\r
728                 try {\r
729                         long pos = getStartPosition(index);\r
730                         b.position(pos);\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
737                 } finally {\r
738                         readUnlock();\r
739                 }\r
740         }\r
741 \r
742         @Override\r
743         public void get(int index, Binding valueBinding, Object dst) throws AccessorException {\r
744                 assert b.isOpen();\r
745                 readLock();\r
746                 try {\r
747                         long pos = getStartPosition(index);\r
748                         b.position(pos);\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
755                 } finally {\r
756                         readUnlock();\r
757                 }\r
758         }\r
759 \r
760         \r
761         @SuppressWarnings("unchecked")\r
762         @Override\r
763         public <T extends Accessor> T getAccessor(int index)\r
764                         throws AccessorConstructionException {\r
765                 assert b.isOpen();\r
766                 readLock();\r
767                 try {\r
768                         b.position(0L);\r
769                         int count = b.readInt();\r
770                         if (index<0 || index>=count) throw new ReferenceException("Element index ("+index+") out of bounds ("+count+")");\r
771                         \r
772                         // Get existing or create new\r
773                         BinaryObject sa = getExistingAccessor(index);\r
774                         if (sa==null) {\r
775                                 long pos = getStartPosition(index);\r
776                                 long len = getLength(index, pos);\r
777 \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
781 \r
782                                 // Add component interest sets\r
783                                 ListenerEntry le = listeners;\r
784                                 while (le!=null) {                              \r
785                                         ArrayInterestSet is = le.getInterestSet();\r
786 \r
787                                         // Generic element interest\r
788                                         InterestSet gis = is.getComponentInterest(); \r
789                                         if (gis != null) {\r
790                                                 try {\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
795                                                 }\r
796                                         }\r
797                                         \r
798                                         // Specific element interest\r
799                                         InterestSet cis = is.getComponentInterest(index); \r
800                                         if (cis != null) {\r
801                                                 try {\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
806                                                 }\r
807                                         }\r
808                                         \r
809                                         // Next listener\r
810                                         le = le.next;\r
811                                 }                               \r
812                                 \r
813                         }\r
814                         \r
815                         return (T) sa;\r
816                 } catch (IOException e) {\r
817                         throw new AccessorConstructionException(e);\r
818                 } catch (AccessorException e) {\r
819                         throw new AccessorConstructionException(e);\r
820                 } finally {\r
821                         readUnlock();\r
822                 }\r
823         }\r
824 \r
825         @SuppressWarnings("unchecked")\r
826         @Override\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
832                         try {\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
837                                 return (T) result;\r
838                         } catch ( NumberFormatException nfe ) {\r
839                                 throw new ReferenceException(nfe);\r
840                         }                       \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
847                         return (T) result;\r
848                 } throw new ReferenceException(reference.getClass().getName()+" is not a reference of an array");       \r
849         }\r
850         \r
851         @Override\r
852         public void getAll(Binding valueBinding, Object[] array)\r
853                         throws AccessorException {\r
854                 assert b.isOpen();\r
855                 readLock();\r
856                 try {\r
857                         b.position(0L);\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
863                         }\r
864                 } catch (IOException e) {\r
865                         throw new AccessorException( e );\r
866                 } catch (SerializerConstructionException e) {\r
867                         throw new AccessorException( e );\r
868                 } finally {\r
869                         readUnlock();\r
870                 }\r
871         }\r
872 \r
873         @Override\r
874         public void getAll(Binding valueBinding, Collection<Object> values)\r
875                         throws AccessorException {\r
876                 assert b.isOpen();\r
877                 readLock();\r
878                 try {\r
879                         b.position(0L);\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
884                         }\r
885                 } catch (IOException e) {\r
886                         throw new AccessorException( e );\r
887                 } catch (SerializerConstructionException e) {\r
888                         throw new AccessorException( e );\r
889                 } finally {\r
890                         readUnlock();\r
891                 }\r
892         }\r
893         \r
894         @Override\r
895         public void setSizeNoflush(int newSize) throws AccessorException {\r
896                 assert b.isOpen();\r
897                 writeLock();\r
898                 try {\r
899                         int oldSize = size();\r
900 \r
901                         // Remove instances  \r
902                         if (newSize<oldSize) {\r
903                                 remove(newSize, oldSize-newSize);\r
904                         }\r
905                                                 \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
911                         }\r
912                         \r
913                 } catch (BindingException e) {\r
914                         throw new AccessorException( e );\r
915                 } finally {\r
916                         writeUnlock();\r
917                 }\r
918         }\r
919 \r
920         @Override\r
921         public void setSize(int newSize) throws AccessorException {\r
922                 assert b.isOpen();\r
923                 writeLock();\r
924                 try {\r
925                         setSizeNoflush(newSize);\r
926                         b.flush();\r
927                 } catch (IOException e) {\r
928                         throw new AccessorException( e );\r
929                 } finally {\r
930                         writeUnlock();\r
931                 }\r
932         }\r
933         \r
934         @Override\r
935         public int size() throws AccessorException {\r
936                 assert b.isOpen();\r
937                 readLock();\r
938                 return index.size();\r
939         }\r
940 \r
941         @Override\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
948                 } else          \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
959                         }\r
960                         removeNoflush(ar.index, 1);\r
961                 } else {\r
962                         throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Array");\r
963                 }\r
964                 \r
965                 return rollback;\r
966         }\r
967 \r
968         @Override\r
969         public void add(Binding binding, Object value) throws AccessorException {\r
970                 assert b.isOpen();\r
971                 writeLock();\r
972                 try {\r
973                         addNoflush(binding, value);\r
974                         b.flush();\r
975                 } catch (IOException e) {\r
976                         throw new AccessorException( e );\r
977                 } finally {\r
978                         writeUnlock();\r
979                 }\r
980         }\r
981 \r
982         @Override\r
983         public void add(int index, Binding binding, Object value)\r
984                         throws AccessorException {\r
985                 assert b.isOpen();\r
986                 writeLock();\r
987                 try {\r
988                         addNoflush(index, binding, value);\r
989                         b.flush();\r
990                 } catch (IOException e) {\r
991                         throw new AccessorException( e );\r
992                 } finally {\r
993                         writeUnlock();\r
994                 }\r
995         }\r
996 \r
997         @Override\r
998         public void addAll(Binding binding, Object[] values)\r
999                         throws AccessorException {\r
1000                 assert b.isOpen();\r
1001                 writeLock();\r
1002                 try {\r
1003                         addAllNoflush(binding, values);\r
1004                         b.flush();\r
1005                 } catch (IOException e) {\r
1006                         throw new AccessorException( e );\r
1007                 } finally {\r
1008                         writeUnlock();\r
1009                 }\r
1010         }\r
1011 \r
1012         @Override\r
1013         public void addAll(int index, Binding binding, Object[] values)\r
1014                         throws AccessorException {\r
1015                 assert b.isOpen();\r
1016                 writeLock();\r
1017                 try {\r
1018                         addAllNoflush(index, binding, values);\r
1019                         b.flush();\r
1020                 } catch (IOException e) {\r
1021                         throw new AccessorException( e );\r
1022                 } finally {\r
1023                         writeUnlock();                  \r
1024                 }\r
1025         }\r
1026 \r
1027         @Override\r
1028         public void remove(int index, int count) throws AccessorException {\r
1029                 assert b.isOpen();\r
1030                 writeLock();\r
1031                 try {\r
1032                         removeNoflush(index, count);\r
1033                         b.flush();\r
1034                 } catch (IOException e) {\r
1035                         throw new AccessorException( e );\r
1036                 } finally {\r
1037                         writeUnlock();\r
1038                 }\r
1039         }\r
1040 \r
1041         @Override\r
1042         public void set(int index, Binding binding, Object value)\r
1043                         throws AccessorException {\r
1044                 assert b.isOpen();\r
1045                 writeLock();\r
1046                 try {\r
1047                         setNoflush(index, binding, value);\r
1048                         b.flush();\r
1049                 } catch (IOException e) {\r
1050                         throw new AccessorException(e);\r
1051                 } finally {\r
1052                         writeUnlock();\r
1053                 }\r
1054         }\r
1055         \r
1056         @Override\r
1057         public void close() throws AccessorException {\r
1058                 if ( this.index instanceof CloseableAccessor ) {\r
1059                         CloseableAccessor ca = (CloseableAccessor) this.index;\r
1060                         ca.close();\r
1061                 }\r
1062                 super.close();\r
1063         }\r
1064         \r
1065 }\r
1066 \r