]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryRecord.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / binary / BinaryRecord.java
1 /*******************************************************************************
2  *  Copyright (c) 2010 Association for Decentralized Information Management in
3  *  Industry THTH ry.
4  *  All rights reserved. This program and the accompanying materials
5  *  are made available under the terms of the Eclipse Public License v1.0
6  *  which accompanies this distribution, and is available at
7  *  http://www.eclipse.org/legal/epl-v10.html
8  *
9  *  Contributors:
10  *      VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.databoard.accessor.binary;
13
14 import java.io.IOException;
15 import java.lang.ref.SoftReference;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.concurrent.Executor;
19
20 import org.simantics.databoard.Bindings;
21 import org.simantics.databoard.accessor.Accessor;
22 import org.simantics.databoard.accessor.RecordAccessor;
23 import org.simantics.databoard.accessor.error.AccessorConstructionException;
24 import org.simantics.databoard.accessor.error.AccessorException;
25 import org.simantics.databoard.accessor.error.ReferenceException;
26 import org.simantics.databoard.accessor.event.Event;
27 import org.simantics.databoard.accessor.event.ValueAssigned;
28 import org.simantics.databoard.accessor.file.FileRecordAccessor;
29 import org.simantics.databoard.accessor.impl.AccessorParams;
30 import org.simantics.databoard.accessor.impl.ListenerEntry;
31 import org.simantics.databoard.accessor.interestset.InterestSet;
32 import org.simantics.databoard.accessor.interestset.RecordInterestSet;
33 import org.simantics.databoard.accessor.reference.ChildReference;
34 import org.simantics.databoard.accessor.reference.IndexReference;
35 import org.simantics.databoard.accessor.reference.LabelReference;
36 import org.simantics.databoard.accessor.reference.NameReference;
37 import org.simantics.databoard.adapter.AdaptException;
38 import org.simantics.databoard.binding.Binding;
39 import org.simantics.databoard.binding.RecordBinding;
40 import org.simantics.databoard.binding.error.BindingConstructionException;
41 import org.simantics.databoard.binding.error.BindingException;
42 import org.simantics.databoard.binding.mutable.MutableVariant;
43 import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;
44 import org.simantics.databoard.serialization.Serializer;
45 import org.simantics.databoard.serialization.SerializerConstructionException;
46 import org.simantics.databoard.type.RecordType;
47 import org.simantics.databoard.util.binary.Blob;
48 import org.simantics.databoard.util.binary.RandomAccessBinary.ByteSide;
49
50 /**
51  * Accessor to a Binary Record.
52  * Get and set field operations scan the file. 
53  * File operations are most efficient if field-specific subaccessors are acquired
54  * and used instead of {@link #setFieldValue(int, Binding, Object)} and
55  * {@link #getFieldValue(int, Binding)}.
56  * <p>
57  * Note, To increase the random access performance of the record, create sub-accessors of
58  * its fields. 
59  *
60  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
61  */
62 public class BinaryRecord extends BinaryObject implements RecordAccessor, FileRecordAccessor {
63
64         /** Accessors to children */
65         java.lang.ref.Reference<BinaryObject>[] children;
66         
67         RecordBinding binding;
68         
69         @SuppressWarnings("unchecked")
70         public BinaryRecord(BinaryObject parent, Blob blob, RecordType type, AccessorParams params) throws AccessorConstructionException {
71                 super(parent, blob, type, params);
72                 if (type.isReferable()) throw new AccessorConstructionException("Refereable record are not supported");
73                 
74                 try {
75                         binding = (RecordBinding) params.bindingScheme.getBinding(type);
76                 } catch (BindingConstructionException e) {
77                         throw new AccessorConstructionException(e);
78                 }               
79                 int count = type.getComponentCount();
80                 children = new java.lang.ref.Reference[count];
81         }
82
83         @Override
84         public RecordType type() {
85                 return (RecordType) type;
86         }       
87         
88         @Override
89         public int count() {
90                 return type().getComponentCount();
91         }
92         
93         /**
94          * Get start position of a field
95          * 
96          * @param fieldIndex
97          * @return
98          * @throws AccessorException
99          */
100         long getStartPosition(int fieldIndex) throws AccessorException {
101                 assert b.isOpen();
102                 readLock();
103                 try {
104                         // sa, saIndex = getFloorExistingAccessor 
105                         BinaryObject sa = getExistingAccessor(fieldIndex);
106                         if (sa!=null) return sa.b.getStartPositionInSourceBinary();
107                         int saIndex = -1;
108                         for (int i=fieldIndex; i>=0; i--) {
109                                 sa = getExistingAccessor(i);
110                                 if (sa!=null) {
111                                         saIndex = i;
112                                         break;
113                                 }
114                         }
115                                 
116                         long pos = saIndex==-1 ? 0L : sa.b.getStartPositionInSourceBinary();
117                         b.position(pos);
118                         if (saIndex<0) saIndex = 0;
119                         for (int i=saIndex; i<fieldIndex; i++) {
120                                 Binding cb = binding.getComponentBinding(i);
121                                 Serializer cs = params.serializerScheme.getSerializer( cb );
122                                 cs.skip(b);
123                         }
124                         return b.position();
125                 } catch (IOException ioe) {
126                         throw new AccessorException( ioe ); 
127                 } catch (SerializerConstructionException e) {
128                         throw new AccessorException( e ); 
129                 } finally {
130                         readUnlock();
131                 }
132         }
133
134         @SuppressWarnings("unchecked")
135         @Override
136         public <T extends Accessor> T getFieldAccessor(String fieldName) throws AccessorConstructionException
137         {
138                 int fieldIndex = type().getComponentIndex(fieldName);
139                 if (fieldIndex<0) throw new AccessorConstructionException("Field "+fieldName+" does not exist");
140                 return (T) getFieldAccessor(fieldIndex);
141         }       
142         
143         @SuppressWarnings("unchecked")
144         @Override
145         public <T extends Accessor> T getFieldAccessor(int index) throws AccessorConstructionException
146         {
147                 if (index<0 || index>=count()) throw new ReferenceException("Field index ("+index+") out of bounds ("+count()+")");
148                 assert b.isOpen();
149                 readLock();
150                 try {
151                         // Get existing or create new
152                         java.lang.ref.Reference<BinaryObject> ref = children[index];
153                         BinaryObject sa = (ref!=null)?(BinaryObject)ref.get():null;
154                         
155                         if (sa==null) {
156                                 // Instantiate new accessor
157                                 Binding cb = binding.getComponentBinding(index);
158                                 Serializer cs = params.serializerScheme.getSerializer( cb ); 
159                                 long pos = getStartPosition(index);
160                                 b.position(pos);
161                                 cs.skip(b);
162                                 long len = b.position() - pos;
163                                 
164                                 sa = createSubAccessor(cb.type(), pos, len, params);
165                                 children[index] = new SoftReference<BinaryObject>(sa);
166
167                                 // Add component interest sets
168                                 ListenerEntry le = listeners;
169                                 while (le!=null) {                              
170                                         RecordInterestSet is = le.getInterestSet();
171                                         InterestSet cis = is.getComponentInterest(index); 
172                                         if (cis != null) {
173                                                 try {
174                                                         ChildReference childPath = ChildReference.concatenate( le.path, new IndexReference(index) );
175                                                         sa.addListener(le.listener, cis, childPath, le.executor);
176                                                 } catch (AccessorException e) {
177                                                         throw new AccessorConstructionException(e);
178                                                 }
179                                         }
180                                         le = le.next;
181                                 }                               
182                         }               
183                         
184                         return (T) sa;
185                 } catch (IOException e) {
186                         throw new AccessorConstructionException(e);
187                 } catch (AccessorException e) {
188                         throw new AccessorConstructionException(e);
189                 } catch (SerializerConstructionException e) {
190                         throw new AccessorConstructionException(e);
191                 } finally {
192                         readUnlock();
193                 }
194         }
195
196         /**
197          * Get existing sub accessor
198          * @param index
199          * @return sub-accessor or <code>null</code>
200          */
201         BinaryObject getExistingAccessor(int index)
202         {
203                 if (index<0 || index>=count()) throw new RuntimeException("Field index ("+index+") out of bounds ("+count()+")");
204                 
205                 // Get existing or create new
206                 java.lang.ref.Reference<BinaryObject> ref = children[index];
207                 BinaryObject accessor = (ref!=null)?(BinaryObject)ref.get():null;
208                 return accessor;
209         }       
210         
211         @Override
212         public Object getFieldValue(String fieldName, Binding fieldBinding)
213                         throws AccessorException {
214                 int fieldIndex = type().getComponentIndex(fieldName);
215                 if (fieldIndex<0) throw new AccessorException("Field "+fieldName+" does not exist");
216                 return getFieldValue(fieldIndex, fieldBinding);
217         }
218         
219         @Override
220         public Object getFieldValue(int index, Binding fieldBinding)
221                         throws AccessorException {
222                 assert b.isOpen();
223                 readLock();
224                 try {
225                         b.position( getStartPosition(index) );
226                         List<Object> ids = new ArrayList<Object>(0);
227                         return params.serializerScheme.getSerializer( fieldBinding ).deserialize(b, ids);
228                 } catch (IOException e) {
229                         throw new AccessorException(e);
230                 } catch (RuntimeSerializerConstructionException e) {
231                         throw new AccessorException(e);
232                 } catch (SerializerConstructionException e) {
233                         throw new AccessorException(e);
234                 } finally {
235                         readUnlock();
236                 }
237         }
238         
239         @Override
240         public void setFieldValue(String fieldName, Binding fieldBinding,
241                         Object value) throws AccessorException {
242                 assert b.isOpen();
243                 writeLock();
244                 try {
245                         setFieldValueNoflush(fieldName, fieldBinding, value);
246                         flush();
247                 } finally {
248                         writeUnlock();
249                 }
250         }
251         
252         @Override
253         public void setFieldValueNoflush(String fieldName, Binding fieldBinding,
254                         Object value) throws AccessorException {
255                 assert b.isOpen();
256                 writeLock();
257                 try {
258                         int fieldIndex = type().getComponentIndex(fieldName);
259                         if (fieldIndex<0) throw new AccessorException("Field "+fieldName+" does not exist");
260                         setFieldValue(fieldIndex, fieldBinding, value);
261                 } finally {
262                         writeUnlock();
263                 }
264         }       
265         
266         @Override
267         public void setFieldValue(int index, Binding fieldBinding,
268                         Object value) throws AccessorException {
269                 assert b.isOpen();
270                 writeLock();
271                 try {
272                         setFieldValueNoflush(index, fieldBinding, value);
273                         flush();
274                 } finally {
275                         writeUnlock();
276                 }
277         }       
278
279         @Override
280         public void setFieldValueNoflush(int index, Binding cb, Object cv)
281         throws AccessorException {
282                 assert b.isOpen();
283                 writeLock();
284                 try {                   
285                         
286                         BinaryObject sa = getExistingAccessor(index);                           
287                         if (sa!=null) {
288                                 // Recursive write using existing sub accessor
289                                 sa.setValueNoflush(cb, cv);
290                                 return;
291                         }
292                         
293                         // Init
294                         long pos = getStartPosition(index);
295                         Serializer cs = params.serializerScheme.getSerializer( cb );
296                                                 
297                         // The size might need adjusting
298                         if (cs.getConstantSize() == null) {
299                                 // Old size
300                                 b.position( pos );
301                                 cs.skip(b, null);
302                                 long oldLen = b.position() - pos;
303                                 
304                                 // New size
305                                 long newLen = cs.getSize(cv, null);
306                                 if (newLen>oldLen) {
307                                         b.position(pos+oldLen);
308                                         b.insertBytes(newLen - oldLen, ByteSide.Left);
309                                 } else {
310                                         b.position(pos+newLen);
311                                         b.removeBytes(oldLen - newLen, ByteSide.Left);
312                                 }
313                         }
314                         
315                         // Write
316                         b.position(pos);
317                         cs.serialize(b, null, cv);
318                         
319                         // Notify Listeners
320                         ListenerEntry le = listeners;
321                         while (le!=null) {                              
322                                 RecordInterestSet is = le.getInterestSet();
323                                 if (is.inNotificationsOf(index)) {                                      
324                                         MutableVariant newValue = is.inValuesOf(index) ? new MutableVariant(cb, cv) : null;
325                                         if (is.inValuesOf(index)) newValue = new MutableVariant(cb, cb.isImmutable() ? cv : cb.clone(cv));
326                                         
327                                         Event e = new ValueAssigned(new IndexReference(index), newValue);
328                                         emitEvent(le, e);
329                                 }
330                                 le = le.next;
331                         }                               
332                 } catch (IOException e) {
333                         throw new AccessorException(e);
334                 } catch (AdaptException e) {
335                         throw new AccessorException(e);
336                 } catch (SerializerConstructionException e) {
337                         throw new AccessorException(e);
338                 } finally {
339                         writeUnlock();
340                 }
341         }
342                 
343         @Override
344         public void addListener(Listener listener, InterestSet interestSet,
345                         ChildReference path, Executor executor) throws AccessorException {
346                 RecordInterestSet is = (RecordInterestSet) interestSet;
347                 super.addListener(listener, interestSet, path, executor);
348                 // Apply component interest set to existing sub-accessors
349                 if (is.componentInterests!=null) {
350                         for (int i=0; i<count(); i++) {
351                                 InterestSet cis = is.getComponentInterest(i);
352                                 if (cis==null) continue;
353                                 BinaryObject sa = getExistingAccessor(i);
354                                 if (sa==null) continue;
355                                 ChildReference childPath = ChildReference.concatenate( path, new IndexReference(i));
356                                 sa.addListener(listener, cis, childPath, executor);
357                         }
358                 }
359         }
360         
361         @Override
362         public void removeListener(Listener listener) throws AccessorException {
363                 ListenerEntry e = detachListener(listener);
364                 if (e==null) return;
365                 RecordInterestSet is = (RecordInterestSet) e.interestSet;
366                 
367                 // Apply component interest set to existing sub-accessors
368                 if (is.componentInterests!=null) {
369                         for (int i=0; i<count(); i++) {
370                                 InterestSet cis = is.getComponentInterest(i);
371                                 if (cis==null) continue;
372                                 BinaryObject sa = getExistingAccessor(i);
373                                 if (sa==null) continue;
374                                 sa.removeListener(listener);
375                         }
376                 }
377         }
378
379         @SuppressWarnings("unchecked")
380         @Override
381         public <T extends Accessor> T getComponent(ChildReference reference) throws AccessorConstructionException {
382                 if (reference==null) return (T) this;
383                 
384                 if (reference instanceof LabelReference) {
385                         LabelReference lr = (LabelReference) reference;
386                         String fieldName = lr.label;
387                         Integer index = type().getComponentIndex(fieldName);
388                         if (index==null) throw new ReferenceException("RecordType doesn't have field by name \""+fieldName+"\"");
389                         BinaryObject sa = getFieldAccessor(index);
390                         if (reference.getChildReference() != null) sa = sa.getComponent(reference.getChildReference());
391                         return (T) sa;                  
392                 }               
393                 
394                 if (reference instanceof IndexReference) {
395                         IndexReference ref = (IndexReference) reference;
396                         int index = ref.getIndex();
397                         BinaryObject sa = getFieldAccessor(index);
398                         if (reference.getChildReference() != null) sa = sa.getComponent(reference.getChildReference());
399                         return (T) sa;                  
400                 } 
401                 
402                 if (reference instanceof NameReference) {
403                         NameReference ref = (NameReference) reference;
404                         String fieldName = ref.getName();
405                         Integer index = type().getComponentIndex(fieldName);
406                         if (index==null) throw new ReferenceException("RecordType doesn't have field by name \""+fieldName+"\"");
407                         BinaryObject sa = getFieldAccessor(index);
408                         if (reference.getChildReference() != null) sa = sa.getComponent(reference.getChildReference());
409                         return (T) sa;                  
410                 } 
411                 
412                 throw new ReferenceException(reference.getClass()+" is not a subreference of RecordType");
413                 
414         }
415
416         @Override
417         public void setValueNoflush(Binding binding, Object newValue) throws AccessorException {
418                 assert b.isOpen();
419                 writeLock();
420                 try {
421                         RecordBinding rb = (RecordBinding) binding;
422                         Serializer rs = params.serializerScheme.getSerializer( rb );
423                         
424                         // Write
425                         long newSize = rs.getSize(newValue, null);
426                         b.setLength(newSize);
427                         b.position(0L);
428                         
429                         for (int index=0; index<count(); index++) {
430                                 long pos = b.position();
431                                 Binding cb = rb.getComponentBinding(index);
432                                 Serializer cs = params.serializerScheme.getSerializer( cb );
433                                 Object cv = rb.getComponent(newValue, index);
434                                 cs.serialize(b, cv);
435                                 long len = b.position() - pos;
436                                 BinaryObject sa = getExistingAccessor(index);
437                                 if (sa!=null) {
438                                         sa.b.setPositionInSource(pos, len);
439                                 }
440                                 
441                                 // Notify field specific listeners
442                                 if (listeners!=null) {
443                                         ListenerEntry le = listeners;
444                                         while (le!=null) {                              
445                                                 RecordInterestSet is = le.getInterestSet();
446                                                 if (is.inNotificationsOf(index) /*&& !is.inNotifications()*/) {
447                                                         Event e = new ValueAssigned( new IndexReference( index ), cb, is.inValuesOf(index) ? cv : null);
448                                                         emitEvent(le, e);
449                                                 }
450                                                 le = le.next;
451                                         }                               
452                                 }
453                         }
454                         
455                         // Notify record specific listeners
456                         /*
457                         if (listeners!=null) {
458                                 ListenerEntry le = listeners;
459                                 while (le!=null) {                              
460                                         RecordInterestSet is = le.getInterestSet();
461                                         if (is.inNotifications() && !is.inComponentNotifications()) {
462                                                 Event e = new ValueAssigned( binding, is.inValues() ? newValue : null);
463                                                 le.emitEvent(e);
464                                         }
465                                         le = le.next;
466                                 }                               
467                         }*/
468                         
469
470                 } catch (BindingException e) {
471                         throw new AccessorException(e);
472                 } catch (IOException e) {
473                         throw new AccessorException(e);
474                 } catch (SerializerConstructionException e) {
475                         throw new AccessorException(e);
476                 } finally {
477                         writeUnlock();
478                 }
479         }
480
481         @Override
482         Event applyLocal(Event e, boolean makeRollback) throws AccessorException {
483                 if (e instanceof ValueAssigned) {                       
484                         ValueAssigned va = (ValueAssigned) e;                   
485                         Event rollback = null; 
486                         if (makeRollback) {
487                                 Binding binding = Bindings.getMutableBinding(type());
488                                 rollback = new ValueAssigned(binding, getValue(binding));                               
489                         }
490                         setValueNoflush(va.newValue.getBinding(), va.newValue.getValue());
491                         return rollback;
492                 } else {
493                         throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Record Type");
494                 }
495         }
496                 
497
498 }
499