]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/Accessors.java
Improved Bindings.getBinding(Class) caching for Datatype.class
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / Accessors.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;
13
14 import java.io.File;
15 import java.io.IOException;
16 import java.nio.ByteBuffer;
17 import java.util.concurrent.Executor;
18
19 import org.simantics.databoard.accessor.Accessor;
20 import org.simantics.databoard.accessor.ArrayAccessor;
21 import org.simantics.databoard.accessor.binary.BinaryArray;
22 import org.simantics.databoard.accessor.binary.BinaryObject;
23 import org.simantics.databoard.accessor.binary.BinaryStreamArray;
24 import org.simantics.databoard.accessor.binary.BinaryVariableWidthStreamArray;
25 import org.simantics.databoard.accessor.error.AccessorConstructionException;
26 import org.simantics.databoard.accessor.error.AccessorException;
27 import org.simantics.databoard.accessor.file.FileAccessor;
28 import org.simantics.databoard.accessor.file.FileArrayAccessor;
29 import org.simantics.databoard.accessor.file.FileLibrary;
30 import org.simantics.databoard.accessor.file.FileVariantAccessor;
31 import org.simantics.databoard.accessor.impl.AccessorParams;
32 import org.simantics.databoard.accessor.impl.DirectoryMap;
33 import org.simantics.databoard.accessor.java.JavaObject;
34 import org.simantics.databoard.accessor.reference.ChildReference;
35 import org.simantics.databoard.binding.Binding;
36 import org.simantics.databoard.binding.error.BindingConstructionException;
37 import org.simantics.databoard.binding.error.BindingException;
38 import org.simantics.databoard.binding.mutable.Variant;
39 import org.simantics.databoard.serialization.Serializer;
40 import org.simantics.databoard.serialization.SerializerConstructionException;
41 import org.simantics.databoard.type.ArrayType;
42 import org.simantics.databoard.type.Datatype;
43 import org.simantics.databoard.util.binary.BinaryFile;
44 import org.simantics.databoard.util.binary.BinaryMemory;
45 import org.simantics.databoard.util.binary.Blob;
46 import org.simantics.databoard.util.binary.RandomAccessBinary;
47
48 /**
49  * This is a facade class for accessor services.
50  *
51  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
52  */
53 public class Accessors {
54
55         /**
56          * Open an accessor to byte data.
57          * 
58          * @param binary
59          * @param type
60          * @param params
61          * @return an accessor
62          * @throws AccessorConstructionException 
63          */
64         @SuppressWarnings("unchecked")
65         public static <T extends Accessor> T getAccessor(RandomAccessBinary binary, Datatype type, AccessorParams params) throws AccessorConstructionException {                
66                 return (T) BinaryObject.createAccessor(binary, type, params);
67         }
68         
69         /**
70          * Open an accessor to byte data.
71          * 
72          * @param binary
73          * @param type
74          * @return an accessor
75          * @throws AccessorConstructionException 
76          */
77         @SuppressWarnings("unchecked")
78         public static <T extends Accessor> T getAccessor(RandomAccessBinary binary, Datatype type) throws AccessorConstructionException {               
79                 return (T) BinaryObject.createAccessor(binary, type, AccessorParams.DEFAULT);
80         }       
81         
82         /**
83          * Open an accessor to byte data.
84          * 
85          * @param binary
86          * @param type
87          * @return an accessor
88          * @throws AccessorConstructionException 
89          */
90         @SuppressWarnings("unchecked")
91         public static <T extends Accessor> T getAccessor(byte[] binary, Datatype type) throws AccessorConstructionException {
92                 RandomAccessBinary rab = new BinaryMemory( ByteBuffer.wrap(binary) );
93                 return (T) BinaryObject.createAccessor(rab, type, AccessorParams.DEFAULT);
94         }
95
96         /**
97          * Open an accessor to a Java Object.
98          * <p>
99          * Accessor is disposed by leaving it to garbage collector.
100          * <p>
101          * Do not modify the object outside the accessor as long as you use the accessor.
102          * Exterioir modifications will mix up listening the mechanism.
103          * Also, do not create more than one accessor to one Object. 
104          * <p>
105          * You must provide mutual exclusion locking mechanism to the whole Object Model
106          * in AccessorParams, if you intend to use the accessors in multi-thread environment. 
107          * 
108          * @since 0.5
109          * @param binding
110          * @param value
111          * @return accessor
112          * @throws AccessorConstructionException
113          */
114         @SuppressWarnings("unchecked")
115         public static <T extends Accessor> T getAccessor(Binding binding, Object value, AccessorParams params) 
116         throws AccessorConstructionException {
117                 return (T) JavaObject.createAccessor(null, binding, value, params);
118         }
119         
120         /**
121          * Open an accessor to a Java Object.
122          * <p>
123          * Accessor is disposed by leaving it to garbage collector.
124          * <p>
125          * Do not modify the object outside the accessor as long as you use the accessor.
126          * Exterioir modifications will mix up listening the mechanism.
127          * Also, do not create more than one accessor to one Object. 
128          * <p>
129          * The accessor is not multi-thread accessible. If you intend to use it in 
130          * concurrent multi-thread environment, use the other method where you can 
131          * pass lock objects in AccessorParams. 
132          * 
133          * @since 0.5
134          * @param binding
135          * @param value
136          * @return accessor
137          * @throws AccessorConstructionException
138          */
139         @SuppressWarnings("unchecked")
140         public static <T extends Accessor> T getAccessor(Binding binding, Object value) 
141         throws AccessorConstructionException {
142                 return (T) JavaObject.createAccessor(null, binding, value, AccessorParams.DEFAULT);
143         }
144         
145         /**
146          * Open an accessor to a Varint Object.
147          * <p>
148          * Accessor is disposed by leaving it to garbage collector.
149          * <p>
150          * Do not modify the object outside the accessor as long as you use the accessor.
151          * Exterioir modifications will mix up listening the mechanism.
152          * Also, do not create more than one accessor to one Object. 
153          * <p>
154          * The accessor is not multi-thread accessible. If you intend to use it in 
155          * concurrent multi-thread environment, use the other method where you can 
156          * pass lock objects in AccessorParams. 
157          * 
158          * @since 0.5
159          * @param variant
160          * @return accessor
161          * @throws AccessorConstructionException
162          */
163         @SuppressWarnings("unchecked")
164         public static <T extends Accessor> T getAccessor(Variant variant) 
165         throws AccessorConstructionException {
166                 return (T) JavaObject.createAccessor(null, variant.getBinding(), variant.getValue(), AccessorParams.DEFAULT);
167         }       
168
169         /**
170          * Open an accessor to a Varint Object.
171          * <p>
172          * Accessor is disposed by leaving it to garbage collector.
173          * <p>
174          * Do not modify the object outside the accessor as long as you use the accessor.
175          * Exterioir modifications will mix up listening the mechanism.
176          * Also, do not create more than one accessor to one Object. 
177          * <p>
178          * The accessor is not multi-thread accessible. If you intend to use it in 
179          * concurrent multi-thread environment, use the other method where you can 
180          * pass lock objects in AccessorParams. 
181          * 
182          * @since 0.5
183          * @param binding
184          * @param ref child reference
185          * @return accessor
186          * @throws AccessorConstructionException
187          */
188         @SuppressWarnings("unchecked")
189         public static <T extends Accessor> T getAccessor(Variant variant, ChildReference ref) 
190         throws AccessorConstructionException {
191                 Accessor a = getAccessor( variant );
192                 return a.getComponent( ref );
193         }       
194         
195         /**
196          * Open Accessor to a Java Object. This version reads the type using reflection.
197          * <p>
198          * Accessor is disposed by leaving it to garbage collector.
199          * <p>
200          * Do not modify the object outside the accessor as long as you use the accessor.
201          * Exterioir modifications will mix up listening the mechanism.
202          * Also, do not create more than one accessor to one Object. 
203          * <p>
204          * The accessor is not multi-thread accessible. If you intend to use it in 
205          * concurrent multi-thread environment, use the other method where you can 
206          * pass lock objects in AccessorParams. 
207          * 
208          * @since 0.5
209          * @param value
210          * @return accessor
211          * @throws AccessorConstructionException
212          */
213         @SuppressWarnings("unchecked")
214         public static <T extends Accessor> T getAccessor(Object value) 
215         throws AccessorConstructionException {
216                 try {
217                         Binding binding = Bindings.getBinding(value.getClass());
218                         return (T) JavaObject.createAccessor(null, binding, value, AccessorParams.DEFAULT);
219                 } catch (BindingConstructionException e) {
220                         throw new AccessorConstructionException(e);
221                 }
222         }
223         
224         /**
225          * Open an accessor to a binary file (.dbb). The file is always a variant.
226          * The accessor must be closed by invoking {@link FileAccessor#close()} at 
227          * root or any sub-accessor.
228          * <p>
229          * To share accessors of the same file use {@link FileLibrary} utility.
230          * 
231          * @since 0.5
232          * @param file
233          * @return file accessor
234          * @throws AccessorConstructionException 
235          */
236         public static FileVariantAccessor openAccessor(File file) throws AccessorConstructionException {                
237                 try {                   
238                         BinaryFile bf = new BinaryFile(file);
239                         FileVariantAccessor result = (FileVariantAccessor) BinaryObject.createAccessor(bf, Datatypes.VARIANT, AccessorParams.DEFAULT);
240                         return result;
241                 } catch (IOException e) {
242                         throw new AccessorConstructionException(e);
243                 }
244         }
245
246         /**
247          * Open a stream file (.stm). Stream file is an array of elements, with no 
248          * header. Element size is constant. 
249          * <p>
250          * To create an empty stream file, just create an empty file.
251          * 
252          * @param file
253          * @param type expected array type
254          * @return accessor
255          * @throws AccessorConstructionException
256          */
257         public static FileArrayAccessor openStream(File file, ArrayType type) throws AccessorConstructionException {
258                 return openStream(file, type, "rw");
259         }
260
261         /**
262          * Open a stream file (.stm). Stream file is an array of elements, with no 
263          * header. Element size is constant. 
264          * <p>
265          * To create an empty stream file, just create an empty file.
266          * 
267          * @param file
268          * @param type expected array type
269          * @param mode Mode "r" or "rw"
270          * @return accessor
271          * @throws AccessorConstructionException
272          */
273         public static FileArrayAccessor openStream(File file, ArrayType type, String mode) throws AccessorConstructionException {
274                 return openStream(file, type, mode, null);
275         }
276         
277         /**
278          * Open a stream file (.stm). Stream file is an array of elements, with no 
279          * header. Element size is constant. 
280          * <p>
281          * To create an empty stream file, just create an empty file.
282          * 
283          * @param file
284          * @param type expected array type
285          * @param mode Mode "r" or "rw"
286          * @param index accessor to long array that keeps the index of the variable width stream 
287          * @return accessor
288          * @throws AccessorConstructionException
289          */
290         public static FileArrayAccessor openStream(File file, ArrayType type, String mode, ArrayAccessor index) throws AccessorConstructionException {
291                 try {                   
292                         BinaryFile bf = new BinaryFile(file, mode);
293                         Blob blob = new Blob(bf);
294                         Binding b = Bindings.getBinding(type.componentType);
295                         Serializer s = Bindings.getSerializer(b);
296                         if ( s.getConstantSize() != null ) {
297                                 return new BinaryStreamArray(null, blob, type, AccessorParams.DEFAULT);
298                         } else {
299                                 if ( index == null ) {
300                                         return new BinaryArray(null, blob, type, AccessorParams.DEFAULT);
301                                 } else {
302                                         return new BinaryVariableWidthStreamArray(null, blob, type, AccessorParams.DEFAULT, index);
303                                 }
304                         }
305                 } catch (IOException e) {
306                         throw new AccessorConstructionException(e);
307                 } catch (AccessorException e) {
308                         throw new AccessorConstructionException(e);
309                 } catch (SerializerConstructionException e) {
310                         throw new AccessorConstructionException(e);
311                 }               
312         }
313         
314         /**
315          * Create a new binary file with an empty value and open an accessor.
316          * If file already exists, it is overwritten.
317          * It is recommended that the file extension is .dbb (Databoard Binary)
318          * <p>
319          * The caller must close the file with {@link FileAccessor#close()}.  
320          * 
321          * @since 0.5
322          * @param file
323          * @return an accessor to the root variant node
324          */
325         public static FileVariantAccessor createFile(File file) 
326         throws AccessorConstructionException {
327                 try {
328                         file.createNewFile();
329                         BinaryFile bf = new BinaryFile(file);
330                         FileVariantAccessor result = (FileVariantAccessor) BinaryObject.createAccessor(bf, Datatypes.VARIANT, AccessorParams.DEFAULT);
331                         
332                         // Write initial value
333                         try {
334                                 Binding vb = Bindings.getBinding(void.class);
335                                 Object vv = vb.createDefault();
336                                 result.setContentValue(vb, vv);
337                         } catch (AccessorException e) {
338                                 // Close file
339                                 try { result.close(); } catch (AccessorException e1) {}
340                                 // Rethrow exception
341                                 throw new AccessorConstructionException(e);
342                         } catch (BindingConstructionException e) {
343                                 // Close file
344                                 try { result.close(); } catch (AccessorException e1) {}
345                                 // Rethrow exception
346                                 throw new AccessorConstructionException(e);
347                         } catch (BindingException e) {
348                                 // Close file
349                                 try { result.close(); } catch (AccessorException e1) {}
350                                 // Rethrow exception
351                                 throw new AccessorConstructionException(e);
352                         } 
353                         
354                         return result;
355                 } catch (IOException e) {
356                         throw new AccessorConstructionException(e);
357                 }
358         }       
359
360         /**
361          * Create a binary file (.dbb) and open an accessor. The file is always a variant.
362          * <p>
363          * File is closed with {@link FileAccessor#close()} at root or any 
364          * sub-accessor.
365          * <p>
366          * To share accessors of the same file use {@link FileLibrary} utility.
367          * 
368          * @param <T>
369          * @param file
370          * @param type
371          * @return an accessor to the value node
372          * @throws AccessorConstructionException 
373          */
374         @SuppressWarnings("unchecked")
375         public static <T extends FileAccessor> T createFile(File file, Datatype type) throws AccessorConstructionException
376         {
377                 boolean existed;
378                 BinaryFile bf;
379                 try {
380                         existed = file.createNewFile();
381                         bf = new BinaryFile(file);
382                 } catch (IOException e1) {
383                         throw new AccessorConstructionException(e1);
384                 }
385                 
386                 try {
387                         FileVariantAccessor result = (FileVariantAccessor) BinaryObject.createAccessor(bf, Datatypes.VARIANT, AccessorParams.DEFAULT);
388                         Binding binding = Bindings.getMutableBinding(type);
389                         Object value = binding.createDefault();
390                         result.setContentValue(binding, value);
391                         return (T) result.getContentAccessor();
392                 } catch (BindingException e) {
393                         if (!existed) file.delete();
394                         throw new AccessorConstructionException(e);
395                 } catch (AccessorException e) {
396                         if (!existed) file.delete();
397                         throw new AccessorConstructionException(e);
398                 }
399         }
400         
401         /**
402          * Open a Map(Variant, Variant) accessor to a directory.
403          * Map entries are binary files (.dbb), and the file name is the key with the following encoding: 
404          *   
405          *  Filenames have the following encoding:
406          *    S<string>.dbb   String types
407          *                        Control characters " : < > | ? * \ / % [0..31] [128..] are escaped as %<hex><hex>
408          *                        " "-space is _
409          *    I<integer>.dbb  Integer types
410          *    L<long>.dbb     Long types
411          *    H<hex>.dbb      All other cases the value as binary 
412          * 
413          * The caller must close (FolderMap#close()) the accessor after usage. 
414          * This releases file handles and a directory polling thread.
415          * 
416          * FolderMap is not concurrent-use-safe. Its files are not either.
417          * The user must ensure that the files are not used concurrently from 
418          * different threads.
419          * 
420          * @param directory
421          * @return MapAccessor
422          */
423         public static DirectoryMap openDirectory(File directory) {
424                 return new DirectoryMap(directory);
425         }       
426         
427         /**
428          * Get an executor for current thread
429          * 
430          * @return executor
431          */
432         public static Executor getCurrentThread() {
433                 return Accessors.getCurrentThread();
434         }
435         
436         static Executor CURRENT_THREAD = new Executor() {
437                 @Override
438                 public void execute(Runnable command) {
439                         command.run();
440                 }               
441         };      
442         
443 }
444