]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryObject.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / binary / BinaryObject.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;
13
14 import java.io.File;\r
15 import java.io.IOException;\r
16 import java.util.Collection;\r
17 import java.util.LinkedList;\r
18 import java.util.List;\r
19 import java.util.concurrent.Executor;\r
20 import java.util.concurrent.locks.Lock;\r
21 \r
22 import org.simantics.databoard.accessor.Accessor;\r
23 import org.simantics.databoard.accessor.CloseableAccessor;\r
24 import org.simantics.databoard.accessor.ParametrisedAccessor;\r
25 import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
26 import org.simantics.databoard.accessor.error.AccessorException;\r
27 import org.simantics.databoard.accessor.error.ReferenceException;\r
28 import org.simantics.databoard.accessor.event.Event;\r
29 import org.simantics.databoard.accessor.event.InvalidatedEvent;\r
30 import org.simantics.databoard.accessor.file.FileAccessor;\r
31 import org.simantics.databoard.accessor.impl.AccessorParams;\r
32 import org.simantics.databoard.accessor.impl.ListenerEntry;\r
33 import org.simantics.databoard.accessor.interestset.InterestSet;\r
34 import org.simantics.databoard.accessor.reference.ChildReference;\r
35 import org.simantics.databoard.adapter.AdaptException;\r
36 import org.simantics.databoard.adapter.AdapterConstructionException;\r
37 import org.simantics.databoard.binding.Binding;\r
38 import org.simantics.databoard.serialization.Serializer;\r
39 import org.simantics.databoard.serialization.SerializerConstructionException;\r
40 import org.simantics.databoard.type.ArrayType;\r
41 import org.simantics.databoard.type.BooleanType;\r
42 import org.simantics.databoard.type.ByteType;\r
43 import org.simantics.databoard.type.Datatype;\r
44 import org.simantics.databoard.type.DoubleType;\r
45 import org.simantics.databoard.type.FloatType;\r
46 import org.simantics.databoard.type.IntegerType;\r
47 import org.simantics.databoard.type.LongType;\r
48 import org.simantics.databoard.type.MapType;\r
49 import org.simantics.databoard.type.OptionalType;\r
50 import org.simantics.databoard.type.RecordType;\r
51 import org.simantics.databoard.type.StringType;\r
52 import org.simantics.databoard.type.UnionType;\r
53 import org.simantics.databoard.type.VariantType;\r
54 import org.simantics.databoard.util.binary.BinaryFile;\r
55 import org.simantics.databoard.util.binary.Blob;\r
56 import org.simantics.databoard.util.binary.RandomAccessBinary;\r
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.\r
65  * <p>\r
66  * \r
67  * \r
68  * @see BinaryArray\r
69  * @see BinaryBoolean\r
70  * @see BinaryByte\r
71  * @see BinaryDouble\r
72  * @see BinaryFloat\r
73  * @see BinaryInteger\r
74  * @see BinaryLong\r
75  * @see BinaryMap\r
76  * @see BinaryOptional\r
77  * @see BinaryRecord\r
78  * @see BinaryString\r
79  * @see BinaryUnion\r
80  * @see BinaryVariant\r
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;\r
93         /** File, optional */\r
94         protected File file;
95         /** Accessor params */\r
96         protected AccessorParams params;\r
97
98         BinaryObject(Accessor parent, Blob blob, Datatype type, AccessorParams params) {
99                 this.parent = parent;
100                 this.b = blob;
101                 this.type = type;\r
102                 this.params = params;\r
103                 \r
104                 if (parent!=null && parent instanceof BinaryObject) {\r
105                         file = ((BinaryObject)parent).file();\r
106                 } else {\r
107                         RandomAccessBinary sourceBinary = b.getSource();\r
108                         if (sourceBinary instanceof BinaryFile) {\r
109                                 BinaryFile bf = (BinaryFile) sourceBinary;\r
110                                 file = bf.file();\r
111                         }\r
112                 }\r
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         }\r
126         \r
127         @Override\r
128         public void reset() throws AccessorException {\r
129                 try {\r
130                         b.reset();\r
131                 } catch (IOException e) {\r
132                         throw new AccessorException(e);\r
133                 }\r
134         }\r
135         \r
136         public RandomAccessBinary getSource() {\r
137                 RandomAccessBinary result = b;\r
138                 while (result instanceof Blob) result = ((Blob)result).getParent();\r
139                 return result;\r
140         }
141 \r
142         /**\r
143          * Close the random access file beneath\r
144          */
145         public void close() throws AccessorException {
146                 writeLock();\r
147                 try {
148                         if (parent!=null) {
149                                 ((FileAccessor) parent).close();
150                                 return;
151                         }
152                         
153                         // Root Object\r
154                         if (b==null) return;\r
155                         RandomAccessBinary rab = getSource();
156                         rab.flush();                    
157                         rab.close();\r
158                         b = null;
159                 } catch (IOException e) {
160                         throw new AccessorException(e);
161                 } finally {\r
162                         writeUnlock();\r
163                 }
164         }\r
165         \r
166         public boolean isOpen() {\r
167                 return b.isOpen();\r
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() {\r
176                 return file;
177         }
178         
179         public RandomAccessBinary getBinary() {
180                 return b;
181         }
182         \r
183         @Override\r
184         public AccessorParams getParams() {\r
185                 return params;\r
186         }\r
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 {\r
228                 assert b.isOpen();\r
229                 writeLock();\r
230                 try {
231                         setValueNoflush(binding, newValue);
232                         b.flush();
233                 } catch (IOException e) {
234                         throw new AccessorException(e);
235                 } finally {\r
236                         writeUnlock();\r
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 \r
249         public boolean setValue(ChildReference path, Binding binding, Object obj) throws AccessorException {\r
250                 try {\r
251                         Accessor a = getComponent(path);\r
252                         a.setValue(binding, obj);\r
253                         return true;\r
254                 } catch (ReferenceException re) {\r
255                         return false;\r
256                 } catch (AccessorConstructionException e) {\r
257                         throw new AccessorException(e);\r
258                 }\r
259         }\r
260         
261         @Override
262         public Object getValue(Binding binding) throws AccessorException {\r
263                 assert b.isOpen();\r
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) {\r
272                         throw new AccessorException(e);\r
273                 } finally {\r
274                         readUnlock();\r
275                 }
276         }
277         \r
278         public void getValue(Binding binding, Object obj) throws AccessorException {\r
279                 assert b.isOpen();\r
280                 readLock();\r
281                 try {\r
282                         Serializer s = params.serializerScheme.getSerializer( binding );\r
283                         b.position(0L);\r
284                         s.deserializeTo(b, null, obj);\r
285                 } catch (IOException e) {\r
286                         throw new AccessorException(e);\r
287                 } catch (SerializerConstructionException e) {\r
288                         throw new AccessorException(e);\r
289                 } finally {\r
290                         readUnlock();\r
291                 }\r
292         }\r
293         \r
294         @Override\r
295         public boolean getValue(ChildReference path, Binding binding, Object obj) throws AccessorException {\r
296                 try {\r
297                         Accessor a = getComponent(path);\r
298                         a.getValue(binding, obj);\r
299                         return true;\r
300                 } catch (ReferenceException re) {\r
301                         return false;\r
302                 } catch (AccessorConstructionException e) {\r
303                         throw new AccessorException(e);\r
304                 }\r
305         }       \r
306         \r
307         public Object getValue(ChildReference path, Binding binding) throws AccessorException {\r
308                 try {\r
309                         Accessor a = getComponent(path);\r
310                         return a.getValue(binding);\r
311                 } catch (ReferenceException re) {\r
312                         return null;\r
313                 } catch (AccessorConstructionException e) {\r
314                         throw new AccessorException(e);\r
315                 }\r
316         }\r
317         \r
318         Object adapt(Object value, Binding domain, Binding range) throws AdaptException, AdapterConstructionException {\r
319                 return params.adapterScheme.getAdapter(domain, range, true, false).adapt(value);\r
320         }       \r
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();\r
351                 writeLock();\r
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 {\r
380                         writeUnlock();\r
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)+")";\r
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) {\r
400 //                      return "Accessor(error="+e.getMessage()+")";\r
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);\r
447                         }                                               
448                         \r
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         \r
501         protected void emitEvent(ListenerEntry le, Event e) {           \r
502                 e.reference = ChildReference.concatenate(le.path, e.reference);\r
503                 le.emitEvent(e);\r
504         }       \r
505 \r
506         protected void emitEvents(ListenerEntry le, Collection<Event> events) {\r
507                 for (Event e : events)\r
508                         e.reference = ChildReference.concatenate(le.path, e.reference);\r
509                 le.emitEvents(events);\r
510         }       \r
511         \r
512         /**\r
513          * Get lock if available. \r
514          * \r
515          * @return lock or <tt>null</tt>\r
516          */\r
517         public Lock getReadLock() {\r
518                 return params.readLock;\r
519         }\r
520         \r
521         /**\r
522          * Get lock if available. \r
523          * \r
524          * @return lock or <tt>null</tt>\r
525          */\r
526         public Lock getWriteLock() {\r
527                 return params.writeLock;\r
528         }\r
529         \r
530 \r
531         /**\r
532          * Lock the lock if there is a lock.\r
533          */\r
534         protected void readLock() {\r
535                 if (params.readLock!=null) params.readLock.lock();\r
536         }\r
537         \r
538         /**\r
539          * Unlock the lock if one exists\r
540          */\r
541         protected void readUnlock() {\r
542                 if (params.readLock!=null) params.readLock.unlock();\r
543         }\r
544 \r
545         /**\r
546          * Lock the lock if there is a lock.\r
547          */\r
548         protected void writeLock() {\r
549                 if (params.writeLock!=null) params.writeLock.lock();\r
550         }\r
551         \r
552         /**\r
553          * Unlock the lock if one exists\r
554          */\r
555         protected void writeUnlock() {\r
556                 if (params.writeLock!=null) params.writeLock.unlock();\r
557         }\r
558         \r
559         
560 }
561