]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryObject.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / binary / BinaryObject.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.File;
15 import java.io.IOException;
16 import java.util.Collection;
17 import java.util.LinkedList;
18 import java.util.List;
19 import java.util.concurrent.Executor;
20 import java.util.concurrent.locks.Lock;
21
22 import org.simantics.databoard.accessor.Accessor;
23 import org.simantics.databoard.accessor.CloseableAccessor;
24 import org.simantics.databoard.accessor.ParametrisedAccessor;
25 import org.simantics.databoard.accessor.error.AccessorConstructionException;
26 import org.simantics.databoard.accessor.error.AccessorException;
27 import org.simantics.databoard.accessor.error.ReferenceException;
28 import org.simantics.databoard.accessor.event.Event;
29 import org.simantics.databoard.accessor.event.InvalidatedEvent;
30 import org.simantics.databoard.accessor.file.FileAccessor;
31 import org.simantics.databoard.accessor.impl.AccessorParams;
32 import org.simantics.databoard.accessor.impl.ListenerEntry;
33 import org.simantics.databoard.accessor.interestset.InterestSet;
34 import org.simantics.databoard.accessor.reference.ChildReference;
35 import org.simantics.databoard.adapter.AdaptException;
36 import org.simantics.databoard.adapter.AdapterConstructionException;
37 import org.simantics.databoard.binding.Binding;
38 import org.simantics.databoard.serialization.Serializer;
39 import org.simantics.databoard.serialization.SerializerConstructionException;
40 import org.simantics.databoard.type.ArrayType;
41 import org.simantics.databoard.type.BooleanType;
42 import org.simantics.databoard.type.ByteType;
43 import org.simantics.databoard.type.Datatype;
44 import org.simantics.databoard.type.DoubleType;
45 import org.simantics.databoard.type.FloatType;
46 import org.simantics.databoard.type.IntegerType;
47 import org.simantics.databoard.type.LongType;
48 import org.simantics.databoard.type.MapType;
49 import org.simantics.databoard.type.OptionalType;
50 import org.simantics.databoard.type.RecordType;
51 import org.simantics.databoard.type.StringType;
52 import org.simantics.databoard.type.UnionType;
53 import org.simantics.databoard.type.VariantType;
54 import org.simantics.databoard.util.binary.BinaryFile;
55 import org.simantics.databoard.util.binary.Blob;
56 import org.simantics.databoard.util.binary.RandomAccessBinary;
57
58 /**
59  * BinaryObject is an accessor to a binary object, usually a random access file.
60  * BinaryObject cannot handle files of recursive types.  
61  * <p>
62  * The file can be opened once. It may not be modified by any other instance other than
63  * accessor while accessors are beign used. You must not create more than one
64  * instance of BinaryObjects for a file.
65  * <p>
66  * 
67  * 
68  * @see BinaryArray
69  * @see BinaryBoolean
70  * @see BinaryByte
71  * @see BinaryDouble
72  * @see BinaryFloat
73  * @see BinaryInteger
74  * @see BinaryLong
75  * @see BinaryMap
76  * @see BinaryOptional
77  * @see BinaryRecord
78  * @see BinaryString
79  * @see BinaryUnion
80  * @see BinaryVariant
81  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
82  */
83 public abstract class BinaryObject implements Accessor, FileAccessor, CloseableAccessor, ParametrisedAccessor {
84     
85         /** Hard link to the parent object, <code>null</code> if this is root */
86         protected Accessor parent;      
87         /** Listeners */
88         protected ListenerEntry listeners = null;
89         /** Random access binary object */
90         protected Blob b;
91         /** Type */
92         protected Datatype type;
93         /** File, optional */
94         protected File file;
95         /** Accessor params */
96         protected AccessorParams params;
97
98         BinaryObject(Accessor parent, Blob blob, Datatype type, AccessorParams params) {
99                 this.parent = parent;
100                 this.b = blob;
101                 this.type = type;
102                 this.params = params;
103                 
104                 if (parent!=null && parent instanceof BinaryObject) {
105                         file = ((BinaryObject)parent).file();
106                 } else {
107                         RandomAccessBinary sourceBinary = b.getSource();
108                         if (sourceBinary instanceof BinaryFile) {
109                                 BinaryFile bf = (BinaryFile) sourceBinary;
110                                 file = bf.file();
111                         }
112                 }
113         }
114         
115         public Datatype type() {
116                 return type;
117         }
118                         
119         public void flush() throws AccessorException {
120                 try {
121                         b.flush();
122                 } catch (IOException e) {
123                         throw new AccessorException(e);
124                 }
125         }
126         
127         @Override
128         public void reset() throws AccessorException {
129                 try {
130                         b.reset();
131                 } catch (IOException e) {
132                         throw new AccessorException(e);
133                 }
134         }
135         
136         public RandomAccessBinary getSource() {
137                 RandomAccessBinary result = b;
138                 while (result instanceof Blob) result = ((Blob)result).getParent();
139                 return result;
140         }
141
142         /**
143          * Close the random access file beneath
144          */
145         public void close() throws AccessorException {
146                 writeLock();
147                 try {
148                         if (parent!=null) {
149                                 ((FileAccessor) parent).close();
150                                 return;
151                         }
152                         
153                         // Root Object
154                         if (b==null) return;
155                         RandomAccessBinary rab = getSource();
156                         rab.flush();                    
157                         rab.close();
158                         b = null;
159                 } catch (IOException e) {
160                         throw new AccessorException(e);
161                 } finally {
162                         writeUnlock();
163                 }
164         }
165         
166         public boolean isOpen() {
167                 return b.isOpen();
168         }
169         
170         /**
171          * Get file if the binary object is based on binary file. 
172          * 
173          * @return file or <code>null</code>
174          */
175         public File file() {
176                 return file;
177         }
178         
179         public RandomAccessBinary getBinary() {
180                 return b;
181         }
182         
183         @Override
184         public AccessorParams getParams() {
185                 return params;
186         }
187         
188         @Override
189         public void addListener(Listener listener, InterestSet interestSet, ChildReference path, Executor executor) throws AccessorException {
190                 listeners = ListenerEntry.link(listeners, listener, interestSet, path, executor);
191         }
192                 
193         protected ListenerEntry detachListener(Listener listener) throws AccessorException {
194                 ListenerEntry e = listeners;
195                 ListenerEntry p = null;
196                 while (e!=null) {
197                         // Found match
198                         if (e.listener == listener) {
199                                 // The match was the first entry of the linked list
200                                 if (p==null) {
201                                         listeners = e.next;
202                                         return e;
203                                 }
204                                 // Some other entry, unlink e
205                                 p.next = e.next;
206                                 return e;
207                         }
208                         p = e;
209                         e = e.next;
210                 }
211                 return null;            
212         }
213         
214         @Override
215         public void removeListener(Listener listener) throws AccessorException {
216                 detachListener(listener);
217         }       
218         
219         /**
220          * Write a new value and flush the buffer.
221          * 
222          * @param binding
223          * @param newValue
224          */
225         @Override
226         public void setValue(Binding binding, Object newValue)
227         throws AccessorException {
228                 assert b.isOpen();
229                 writeLock();
230                 try {
231                         setValueNoflush(binding, newValue);
232                         b.flush();
233                 } catch (IOException e) {
234                         throw new AccessorException(e);
235                 } finally {
236                         writeUnlock();
237                 }
238         }
239         
240         /**
241          * Write a new value and don't flush the buffer
242          * 
243          * @param binding
244          * @param newValue
245          * @throws AccessorException
246          */
247         public abstract void setValueNoflush(Binding binding, Object newValue) throws AccessorException;
248
249         public boolean setValue(ChildReference path, Binding binding, Object obj) throws AccessorException {
250                 try {
251                         Accessor a = getComponent(path);
252                         a.setValue(binding, obj);
253                         return true;
254                 } catch (ReferenceException re) {
255                         return false;
256                 } catch (AccessorConstructionException e) {
257                         throw new AccessorException(e);
258                 }
259         }
260         
261         @Override
262         public Object getValue(Binding binding) throws AccessorException {
263                 assert b.isOpen();
264                 readLock();
265                 try {
266                         Serializer s = params.serializerScheme.getSerializer( binding );
267                         b.position(0L);
268                         return s.deserialize(b, null);
269                 } catch (IOException e) {
270                         throw new AccessorException(e);
271                 } catch (SerializerConstructionException e) {
272                         throw new AccessorException(e);
273                 } finally {
274                         readUnlock();
275                 }
276         }
277         
278         public void getValue(Binding binding, Object obj) throws AccessorException {
279                 assert b.isOpen();
280                 readLock();
281                 try {
282                         Serializer s = params.serializerScheme.getSerializer( binding );
283                         b.position(0L);
284                         s.deserializeTo(b, null, obj);
285                 } catch (IOException e) {
286                         throw new AccessorException(e);
287                 } catch (SerializerConstructionException e) {
288                         throw new AccessorException(e);
289                 } finally {
290                         readUnlock();
291                 }
292         }
293         
294         @Override
295         public boolean getValue(ChildReference path, Binding binding, Object obj) throws AccessorException {
296                 try {
297                         Accessor a = getComponent(path);
298                         a.getValue(binding, obj);
299                         return true;
300                 } catch (ReferenceException re) {
301                         return false;
302                 } catch (AccessorConstructionException e) {
303                         throw new AccessorException(e);
304                 }
305         }       
306         
307         public Object getValue(ChildReference path, Binding binding) throws AccessorException {
308                 try {
309                         Accessor a = getComponent(path);
310                         return a.getValue(binding);
311                 } catch (ReferenceException re) {
312                         return null;
313                 } catch (AccessorConstructionException e) {
314                         throw new AccessorException(e);
315                 }
316         }
317         
318         Object adapt(Object value, Binding domain, Binding range) throws AdaptException, AdapterConstructionException {
319                 return params.adapterScheme.getAdapter(domain, range, true, false).adapt(value);
320         }       
321         
322         /**
323          * Send notification that this accessor has been detached from the parent
324          */
325         void invalidatedNotification() {
326                 ListenerEntry le = listeners;
327                 while (le!=null) {                      
328                         InterestSet is = le.getInterestSet();
329                         if (is.inNotifications()) {
330                                 InvalidatedEvent e = new InvalidatedEvent();
331                                 emitEvent(le, e);
332                         }
333                         le = le.next;
334                 }               
335         }       
336         
337         /**
338          * Apply a change set that has events for the particular accessor. 
339          * There are no sub-accessor events. Does not flush buffer.
340          * 
341          * @param cs
342          * @param makeRollback
343          * @return rollback-event
344          * @throws AccessorException
345          */
346         abstract Event applyLocal(Event e, boolean makeRollback) throws AccessorException;
347         
348         @Override
349         public void apply(List<Event> cs, LinkedList<Event> rollback) throws AccessorException {
350                 assert b.isOpen();
351                 writeLock();
352                 try {
353                         boolean makeRollback = rollback != null;
354                         for (Event e : cs) {
355                                 // Accessor
356                                 BinaryObject a = e.reference == null ? this : (BinaryObject) getComponent(e.reference);
357                                 // Apply changes                                
358                                 Event rbe = a.applyLocal(e, makeRollback);
359                                 if (makeRollback) {
360                                         rbe.reference = e.reference;
361                                         rollback.addFirst( rbe );
362                                 }
363                         }
364                         // Flush after successful transaction
365                         try {
366                                 b.flush();
367                         } catch (IOException e1) {
368                                 throw new AccessorException(e1);
369                         }
370                 } catch (AccessorConstructionException ae) {
371                         // Attempt to flush, don't report if fails
372                         // The contract is that all events that did go thru, created a rollback
373                         // and those must be flushed
374                         try {
375                                 b.flush();
376                         } catch (IOException e1) {
377                         }
378                         throw new AccessorException(ae);
379                 } finally {
380                         writeUnlock();
381                 }
382                 
383         }       
384         
385         @Override
386         public String toString() {              
387 //              try {
388                         Datatype type = type();
389 //                      Binding binding = params.bindingScheme.getBinding(type);
390 //                      Object instance = getValue(binding);
391 //                      return "Accessor("+binding.printValueDefinition(instance, true)+")";
392                         return this.getClass()+"("+type+")";
393 //              } catch (AccessorException e) {
394 //                      return "Accessor(error="+e.getMessage()+")";
395 //              } catch (BindingException e) {
396 //                      return "Accessor(error="+e.getMessage()+")";
397 //              } catch (IOException e) {
398 //                      return "Accessor(error="+e.getMessage()+")";
399 //              } catch (BindingConstructionException e) {
400 //                      return "Accessor(error="+e.getMessage()+")";
401 //              }
402         }               
403         
404         public static BinaryObject createAccessor(RandomAccessBinary binary, Datatype type, AccessorParams params) throws AccessorConstructionException
405         {
406                 try {
407                         Blob blob = binary instanceof Blob ? (Blob) binary : new Blob(binary);
408
409                         if (type instanceof BooleanType) {
410                                 return new BinaryBoolean(null, blob, (BooleanType) type, params);
411                         }
412                         if (type instanceof ByteType) {
413                                 return new BinaryByte(null, blob, (ByteType)type, params);
414                         }
415                         if (type instanceof IntegerType) {
416                                 return new BinaryInteger(null, blob, (IntegerType)type, params);
417                         }
418                         if (type instanceof LongType) {
419                                 return new BinaryLong(null, blob, (LongType)type, params);
420                         }
421                         if (type instanceof FloatType) {
422                                 return new BinaryFloat(null, blob, (FloatType)type, params);
423                         }
424                         if (type instanceof DoubleType) {
425                                 return new BinaryDouble(null, blob, (DoubleType)type, params);
426                         }
427                         if (type instanceof StringType) {
428                                 return new BinaryString(null, blob, (StringType)type, params);
429                         }                       
430                         if (type instanceof OptionalType) {
431                                 return new BinaryOptional(null, blob, (OptionalType)type, params);
432                         }                       
433                         if (type instanceof UnionType) {
434                                 return new BinaryUnion(null, blob, (UnionType)type, params);
435                         }                       
436                         if (type instanceof RecordType) {
437                                 return new BinaryRecord(null, blob, (RecordType)type, params);
438                         }                       
439                         if (type instanceof VariantType) {
440                                 return new BinaryVariant(null, blob, (VariantType)type, params);
441                         }                       
442                         if (type instanceof MapType) {
443                                 return new BinaryMap(null, blob, (MapType)type, params);
444                         }                       
445                         if (type instanceof ArrayType) {
446                                 return new BinaryArray(null, blob, (ArrayType)type, params);
447                         }                                               
448                         
449                         throw new AccessorConstructionException("Can not create accessor to "+type);
450                 } catch (IOException e) {
451                         throw new AccessorConstructionException(e);
452                 }
453         }
454
455         BinaryObject createSubAccessor(Datatype type, long position, long length, AccessorParams params) 
456         throws AccessorConstructionException {
457                 Blob sb = b.createSubBlob(position, length);            
458                 
459                 if (type instanceof BooleanType) {
460                         return new BinaryBoolean(this, sb, (BooleanType) type, params);
461                 }
462                 if (type instanceof ByteType) {
463                         return new BinaryByte(this, sb, (ByteType)type, params);
464                 }
465                 if (type instanceof IntegerType) {
466                         return new BinaryInteger(this, sb, (IntegerType)type, params);
467                 }
468                 if (type instanceof LongType) {
469                         return new BinaryLong(this, sb, (LongType)type, params);
470                 }
471                 if (type instanceof FloatType) {
472                         return new BinaryFloat(this, sb, (FloatType)type, params);
473                 }
474                 if (type instanceof DoubleType) {
475                         return new BinaryDouble(this, sb, (DoubleType)type, params);
476                 }
477                 if (type instanceof StringType) {
478                         return new BinaryString(this, sb, (StringType)type, params);
479                 }
480                 if (type instanceof OptionalType) {
481                         return new BinaryOptional(this, sb, (OptionalType)type, params);
482                 }
483                 if (type instanceof UnionType) {
484                         return new BinaryUnion(this, sb, (UnionType)type, params);
485                 }
486                 if (type instanceof VariantType) {
487                         return new BinaryVariant(this, sb, (VariantType)type, params);
488                 }
489                 if (type instanceof ArrayType) {
490                         return new BinaryArray(this, sb, (ArrayType)type, params);
491                 }
492                 if (type instanceof MapType) {
493                         return new BinaryMap(this, sb, (MapType)type, params);
494                 }
495                 if (type instanceof RecordType) {
496                         return new BinaryRecord(this, sb, (RecordType)type, params);
497                 }
498                 throw new AccessorConstructionException("Can not create accessor to "+type);
499         }       
500         
501         protected void emitEvent(ListenerEntry le, Event e) {           
502                 e.reference = ChildReference.concatenate(le.path, e.reference);
503                 le.emitEvent(e);
504         }       
505
506         protected void emitEvents(ListenerEntry le, Collection<Event> events) {
507                 for (Event e : events)
508                         e.reference = ChildReference.concatenate(le.path, e.reference);
509                 le.emitEvents(events);
510         }       
511         
512         /**
513          * Get lock if available. 
514          * 
515          * @return lock or <tt>null</tt>
516          */
517         public Lock getReadLock() {
518                 return params.readLock;
519         }
520         
521         /**
522          * Get lock if available. 
523          * 
524          * @return lock or <tt>null</tt>
525          */
526         public Lock getWriteLock() {
527                 return params.writeLock;
528         }
529         
530
531         /**
532          * Lock the lock if there is a lock.
533          */
534         protected void readLock() {
535                 if (params.readLock!=null) params.readLock.lock();
536         }
537         
538         /**
539          * Unlock the lock if one exists
540          */
541         protected void readUnlock() {
542                 if (params.readLock!=null) params.readLock.unlock();
543         }
544
545         /**
546          * Lock the lock if there is a lock.
547          */
548         protected void writeLock() {
549                 if (params.writeLock!=null) params.writeLock.lock();
550         }
551         
552         /**
553          * Unlock the lock if one exists
554          */
555         protected void writeUnlock() {
556                 if (params.writeLock!=null) params.writeLock.unlock();
557         }
558         
559         
560 }
561