Fixing several binding-related bugs
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / Bindings.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  *      Semantum Oy - gitlab #313
12  *******************************************************************************/
13 package org.simantics.databoard;
14
15 import java.io.IOException;
16 import java.util.Comparator;
17 import java.util.Map;
18
19 import org.simantics.databoard.adapter.AdaptException;
20 import org.simantics.databoard.adapter.Adapter;
21 import org.simantics.databoard.adapter.AdapterConstructionException;
22 import org.simantics.databoard.adapter.AdapterFactory;
23 import org.simantics.databoard.adapter.RuntimeAdaptException;
24 import org.simantics.databoard.adapter.RuntimeAdapterConstructionException;
25 import org.simantics.databoard.annotations.ArgumentImpl;
26 import org.simantics.databoard.annotations.Arguments;
27 import org.simantics.databoard.binding.ArrayBinding;
28 import org.simantics.databoard.binding.Binding;
29 import org.simantics.databoard.binding.BooleanBinding;
30 import org.simantics.databoard.binding.ByteBinding;
31 import org.simantics.databoard.binding.DoubleBinding;
32 import org.simantics.databoard.binding.FloatBinding;
33 import org.simantics.databoard.binding.IntegerBinding;
34 import org.simantics.databoard.binding.LongBinding;
35 import org.simantics.databoard.binding.StringBinding;
36 import org.simantics.databoard.binding.VariantBinding;
37 import org.simantics.databoard.binding.classfactory.TypeClassFactory;
38 import org.simantics.databoard.binding.error.BindingConstructionException;
39 import org.simantics.databoard.binding.error.BindingException;
40 import org.simantics.databoard.binding.error.RuntimeBindingConstructionException;
41 import org.simantics.databoard.binding.factory.BindingRepository;
42 import org.simantics.databoard.binding.factory.TypeBindingFactory;
43 import org.simantics.databoard.binding.impl.BooleanArrayBinding;
44 import org.simantics.databoard.binding.impl.BooleanBindingDefault;
45 import org.simantics.databoard.binding.impl.ByteArrayBinding;
46 import org.simantics.databoard.binding.impl.ByteBindingDefault;
47 import org.simantics.databoard.binding.impl.DoubleArrayBinding;
48 import org.simantics.databoard.binding.impl.DoubleBindingDefault;
49 import org.simantics.databoard.binding.impl.FloatArrayBinding;
50 import org.simantics.databoard.binding.impl.FloatBindingDefault;
51 import org.simantics.databoard.binding.impl.IntArrayBinding;
52 import org.simantics.databoard.binding.impl.IntegerBindingDefault;
53 import org.simantics.databoard.binding.impl.LongArrayBinding;
54 import org.simantics.databoard.binding.impl.LongBindingDefault;
55 import org.simantics.databoard.binding.impl.StringArrayBinding;
56 import org.simantics.databoard.binding.impl.StringBindingDefault;
57 import org.simantics.databoard.binding.impl.UnsignedByteBinding;
58 import org.simantics.databoard.binding.impl.UnsignedIntegerBinding;
59 import org.simantics.databoard.binding.impl.UnsignedLongBinding;
60 import org.simantics.databoard.binding.mutable.MutableBooleanBinding;
61 import org.simantics.databoard.binding.mutable.MutableByteBinding;
62 import org.simantics.databoard.binding.mutable.MutableDoubleBinding;
63 import org.simantics.databoard.binding.mutable.MutableFloatBinding;
64 import org.simantics.databoard.binding.mutable.MutableIntegerBinding;
65 import org.simantics.databoard.binding.mutable.MutableLongBinding;
66 import org.simantics.databoard.binding.mutable.MutableStringBinding;
67 import org.simantics.databoard.binding.mutable.MutableVariantBinding;
68 import org.simantics.databoard.binding.reflection.BindingProvider;
69 import org.simantics.databoard.binding.reflection.BindingRequest;
70 import org.simantics.databoard.binding.reflection.ClassBindingFactory;
71 import org.simantics.databoard.binding.reflection.VoidBinding;
72 import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;
73 import org.simantics.databoard.serialization.Serializer;
74 import org.simantics.databoard.serialization.SerializerConstructionException;
75 import org.simantics.databoard.serialization.SerializerScheme;
76 import org.simantics.databoard.type.ArrayType;
77 import org.simantics.databoard.type.Datatype;
78 import org.simantics.databoard.util.DataValueUtil;
79
80 /**
81  * This ia a facade class for the binding services.
82  *
83  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
84  */
85 public class Bindings {
86         
87         public static final Databoard databoard;
88
89         // Repositories
90
91         /** Repository of mutable bindings */
92     public static final Map<Datatype, Binding>       mutableBindingRepository;
93
94     /** Repository of default bindings */
95     public static final Map<Datatype, Binding>       defaultBindingRepository;
96
97     /** Repository of class Bindings */
98     public static final BindingRepository            bindingRepository;
99
100     /** Repository of serializers */
101     public static final Map<Binding, Serializer>     serializerRepository;
102         
103     
104     // Factories
105
106     /** Mutable Bindings Factory */
107         public static final TypeBindingFactory           mutableBindingFactory;
108
109         /** Default Bindings Factory */
110         public static final TypeBindingFactory           defaultBindingFactory;
111
112         /** Reflection based Binding Factory, create binding to class */
113     public static final ClassBindingFactory              classBindingFactory;
114
115     /** Serializer Factory */
116     public static final SerializerScheme                 serializationFactory;
117
118     /** Adapter Factory */
119         public static final AdapterFactory               adapterFactory;
120         
121         /** Class Factory */ 
122         public static final TypeClassFactory                    typeClassFactory;
123         
124         
125         // Bindings to various primitives
126         public static final StringBinding  STRING;                      // java.lang.String     
127         public static final IntegerBinding INTEGER;                     // java.lang.Integer    
128         public static final BooleanBinding BOOLEAN;                     // java.lang.Boolean    
129         public static final ByteBinding    BYTE;                        // java.lang.Byte       
130         public static final LongBinding    LONG;                        // java.lang.Long
131         public static final DoubleBinding  DOUBLE;                      // java.lang.Double     
132         public static final FloatBinding   FLOAT;                       // java.lang.Float      
133         public static final VariantBinding VARIANT;                     // Variant      
134         public static final VariantBinding OBJECT;                      // java.lang.Object ( as variant )      
135         public static final VariantBinding STR_VARIANT;         // java.lang.String ( as variant )
136         
137         public static final Binding        VOID;            // void ( as {} )
138         public static final Binding        BEAN;            // Bean ( as variant )
139         public static final Binding        DATATYPE;        // org.simantics.databoard.type.Datatype
140
141         public static final ArrayBinding   BOOLEAN_ARRAY;       // boolean[]    
142         public static final ArrayBinding   BYTE_ARRAY;          // byte[]
143         public static final ArrayBinding   INT_ARRAY;           // int[]
144         public static final ArrayBinding   LONG_ARRAY;          // long[]
145         public static final ArrayBinding   FLOAT_ARRAY;         // float[]
146         public static final ArrayBinding   DOUBLE_ARRAY;        // double[]
147         public static final ArrayBinding   STRING_ARRAY;        // String[]
148
149         public static final StringBinding  MUTABLE_STRING;      // MutableString
150         public static final IntegerBinding MUTABLE_INTEGER;     // MutableInteger
151         public static final BooleanBinding MUTABLE_BOOLEAN; // MutableBoolean
152         public static final ByteBinding    MUTABLE_BYTE;        // MutableByte
153         public static final LongBinding    MUTABLE_LONG;        // MutableLong
154         public static final FloatBinding   MUTABLE_FLOAT;       // MutableFloat
155         public static final DoubleBinding  MUTABLE_DOUBLE;      // MutableDouble
156         public static final VariantBinding MUTABLE_VARIANT; // MutableVariant
157
158         public static final IntegerBinding UNSIGNED_INTEGER;                    // UnsignedInteger.Immutable
159         public static final ByteBinding    UNSIGNED_BYTE;                               // UnsignedByte.Immutable
160         public static final LongBinding    UNSIGNED_LONG;                               // UnsignedLong.Immutable
161
162         public static final IntegerBinding MUTABLE_UNSIGNED_INTEGER;    // UnsignedInteger.Mutable
163         public static final ByteBinding    MUTABLE_UNSIGNED_BYTE;               // UnsignedByte.Mutable
164         public static final LongBinding    MUTABLE_UNSIGNED_LONG;               // UnsignedLong.Mutable 
165                 
166     /**
167      * Get or create a binding that is completely mutable java class. 
168      * 
169          * DataType           | Class of the bound instance
170          * ===================|==================
171          * UnionType          | GenericBinding.TaggedObject.class
172          * OptionType         | ValueContainer.class
173          * RecordType         | Object[].class
174          * BooleanType        | MutableBoolean.class
175          * DoubleType         | MutableDouble.class
176          * FloatType          | MutableFloat.class
177          * ByteType           | MutableByte.class
178          * IntegerType        | MutableInt.class
179          * LongType           | MutableLong.class
180          * StringType         | ValueContainer.class
181          * ArrayType          | ArrayList.class
182          * MapType            | TreeMap.class
183          * VariantType        | MutableVariant.class
184          * 
185          * Note, requesting a binding with this convenience method stores the 
186          * binding and the type with strong reference, thus preventing garbage 
187          * collection. To allow garbage collection, please use another instance of 
188          * GenericBindingFactory and binding repository (Map<Datatype, Binding>). <p>
189      *
190          * @param type the type to create binding to
191          * @return binding binding to a mutable class 
192      */    
193     @SuppressWarnings("unchecked")
194         public static <T extends Binding> T getMutableBinding(Datatype type) {
195         try {
196                 Binding binding = mutableBindingRepository.get(type);
197                 if (binding!=null) return (T) binding;
198                 synchronized(mutableBindingRepository) {
199                         return (T) mutableBindingFactory.getBinding(type);
200                 }
201                 } catch (BindingConstructionException e) {
202                         // Unexpected - if error is thrown there is fault in GenericBindingScheme
203                         throw new RuntimeBindingConstructionException(e);
204                 }
205     }
206     
207     /**
208      * Get or create a binding based on default java classes, such as 
209      * Integer.class, or byte[].class. The result is often binding for an 
210      * immutable classs. These bindings are more efficient than mutable bindings (above).
211      * 
212          * DataType           | Class of the bound instance
213          * ===================|==================
214      * BooleanType        | Boolean.class
215      * ByteType           | Byte.class
216      * FloatType          | Float.class
217      * DoubleType         | Double.class
218      * IntegerType        | Int.class
219      * LongType           | Long.class
220      * StringType         | String.class
221      * UnionType          | TaggedObject.class
222      * OptionType         | ValueContainer.class
223      * RecordType         | Object[].class
224      * MapType            | TreeMap.class
225      * VariantType        | Variant.class
226      * ArrayType(Boolean) | boolean[].class
227      * ArrayType(Byte)    | byte[].class
228      * ArrayType(Integer) | int[].class
229      * ArrayType(Long)    | long[].class
230      * ArrayType(Float)   | float[].class
231      * ArrayType(Double)  | double[].class
232      * ArrayType(Byte)    | byte[].class
233      * ArrayType( T )     | Object[].class
234      * 
235          * Note, requesting a binding with this convenience method stores the 
236          * binding and the type with strong reference, thus preventing garbage 
237          * collection. To allow garbage collection, please use another instance of 
238          * DefaultBindingFactory and binding repository (Map<Datatype, Binding>). <p>
239      *
240          * @param type the type to create binding to
241          * @return binding binding to a mutable class 
242      */    
243     @SuppressWarnings("unchecked")
244         public static <T extends Binding> T getBinding(Datatype type) {
245         try {
246                 Binding binding = defaultBindingRepository.get(type);
247                 if (binding!=null) return (T) binding;
248                 synchronized(defaultBindingRepository) {
249                         return (T) defaultBindingFactory.getBinding(type);
250                 }
251         } catch (BindingConstructionException e) {
252                         // Unexpected - if error is thrown there is fault in DefaultBindingScheme
253                         throw new RuntimeBindingConstructionException(e);
254                 }
255     }    
256         
257         /**
258          * Get a binding to a Java Class. Details can be added by placing annotations
259          * to the java classes. See more in package org.simantics.databoard.annotations. 
260          * <p>
261          *  
262          * Whether the result is a completely mutable or not depends on the 
263          * requested class. For instance, fields such as Boolean, Integer, Long
264          * are not mutable, instead MutableBoolean, MutableInteger and MutableLong are.
265          * The length of Object[] is not mutable, but length of List<Object> is. <p>
266          * 
267          * Note, requesting a binding with this convenience method stores the 
268          * binding and the class with strong reference, thus preventing garbage 
269          * collection. To allow garbage collection, please use another instance of 
270          * BindingFactory and binding repository (Map<BindingRequest, Binding>). <p>
271          * 
272          * Is asm library is available, the binding is bytecode generated. Then read
273          * and write operations are direct get/set calls or direct field read/writes. 
274          * There is no reflection used. <p>
275          * 
276          * @see ClassBindingFactory  
277          * @param clazz 
278          * @return binding
279          * @throws BindingConstructionException
280          */
281     @SuppressWarnings("unchecked")
282         public static <T extends Binding> T getBinding(Class<?> clazz)
283     throws BindingConstructionException
284     {
285         Binding binding = bindingRepository.get( clazz );
286         if (binding != null) {
287                 return (T) binding;
288         }
289         
290         BindingRequest request = new BindingRequest( clazz );
291         synchronized(classBindingFactory) {
292                 binding = classBindingFactory.construct(request);
293         }
294                 return (T) binding;
295     }
296
297     @SuppressWarnings("unchecked")
298         public static <T extends Binding> T getBinding(BindingRequest request)
299     throws BindingConstructionException
300     {
301         synchronized(classBindingFactory) {
302                 return (T) classBindingFactory.construct(request);
303         }
304     }
305     
306         /**
307          * Get a binding for a Java Class. Use this method to acquire class 
308          * parameters for a generics class. <p>
309          * 
310          * Example 1: 
311          * 
312          *    Binding binding = Bindings.getBinding(Map.class, String.class, Integer.class);
313          *    Map<String, Integer> map = (Map<String, Integer>) binding.createDefault();
314          *    
315          * Example 2:
316          *    
317      *  Binding d = Bindings.getBinding(List.class, Integer.class);
318          *  List<Integer> list = (List<Integer>) d.createRandom(5);
319          *    
320          * Example 3:
321          *    
322      *  Binding d = Bindings.getBinding(List.class, List.class, Integer.class);
323          *  List<List<Integer>> list = (List<List<Integer>>) d.createRandom(5);
324          * 
325          * @see ClassBindingFactory  
326          * @param clazz
327          * @return binding
328          * @throws BindingConstructionException
329          */
330     @SuppressWarnings("unchecked")
331         public static <T extends Binding> T getBinding(Class<?> clazz, Class<?>...parameters)
332     throws BindingConstructionException
333     {
334         BindingRequest request = new BindingRequest( clazz, parameters );
335         synchronized(classBindingFactory) {
336                 return (T) classBindingFactory.construct(request);
337         }
338     }
339
340     /**
341      * Try to get a binding for the actual class of a Java object.
342      * @param obj  A Java object
343      * @return  A binding for the class of the object
344      * @throws BindingConstructionException  if no binding can be constructed
345      */
346     public static <T extends Binding> T getInstanceBinding(Object obj)
347     throws BindingConstructionException
348     {
349         return getBinding(obj.getClass());
350     }
351     
352     /**
353          * Read binding and type from a class. DataType details and parameters
354          * are read as annotations placed in the class.  
355          * (See org.simantics.databoard.annotations)
356          * <p>
357          * As an exception, in the subclasses of {@link Throwable}, the fields of
358          * Throwable are omited.
359          * <p>
360          * This method is used for well-known classes where the caller is 100% sure
361          * that a binding is construable without exception. <p> 
362          * 
363          * @param clazz
364          * @return binding
365          * @throws RuntimeBindingConstructionException
366      */
367         @SuppressWarnings("unchecked")
368         public static <T extends Binding> T getBindingUnchecked(Class<?> clazz)
369     throws RuntimeBindingConstructionException
370     {
371                 try {
372                 return (T) getBinding(clazz);
373                 } catch (BindingConstructionException e) {
374                         throw new RuntimeBindingConstructionException(e);
375                 }
376     }    
377
378         /**
379          * Get a binding for a Java Class. Use this method to acquire class 
380          * parameters for a generics class. <p>
381          * 
382          * Example 1: 
383          * 
384          *    Binding binding = Bindings.getBinding(Map.class, String.class, Integer.class);
385          *    Map<String, Integer> map = (Map<String, Integer>) binding.createDefault();
386          *    
387          * Example 2:
388          *    
389      *  Binding d = Bindings.getBinding(List.class, Integer.class);
390          *  List<Integer> list = (List<Integer>) d.createRandom(5);
391          *    
392          * Example 3:
393          *    
394      *  Binding d = Bindings.getBinding(List.class, List.class, Integer.class);
395          *  List<List<Integer>> list = (List<List<Integer>>) d.createRandom(5);
396          * 
397          * @see ClassBindingFactory  
398          * @param clazz
399          * @return binding
400          * @throws BindingConstructionException
401          */
402     @SuppressWarnings("unchecked")
403         public static <T extends Binding> T getBindingUnchecked(Class<?> clazz, Class<?>...parameters)
404     throws RuntimeBindingConstructionException
405     {
406         try {
407                 Arguments args = new ArgumentImpl(parameters);
408                 BindingRequest request = new BindingRequest( clazz, args );
409                 Binding binding = bindingRepository.get( request );
410                 if (binding!=null);
411                 synchronized(classBindingFactory) {
412                         binding = classBindingFactory.construct(request);
413                 }
414                         return (T) binding;
415         } catch (BindingConstructionException e) {
416                 throw new RuntimeBindingConstructionException(e);
417         }
418     }    
419     
420     /**
421      * Add a simple binding to reflection binding factory.
422      *  
423      * @param binding
424      * @param clazz
425      * @param parameters parameter classes
426      */
427     public static void addBinding( Binding binding, Class<?> clazz, Class<?>...parameters )
428     {
429         ArgumentImpl args = new ArgumentImpl( parameters );
430         BindingRequest request = new BindingRequest( clazz, args );
431         bindingRepository.put( request, binding );
432     }
433     
434     /**
435      * Add binding factory for compositive bindings 
436      * 
437      * @param factory
438      */
439     public static void addBindingFactory( BindingProvider factory )
440     {
441         classBindingFactory.addFactory( factory );
442     }
443     
444     /**
445      * Creates a bean class 
446      * @param type
447      * @return class
448      */
449     public static BindingRequest getBeanBindingRequest( Datatype type ) throws RuntimeBindingConstructionException {
450         try {
451                         return typeClassFactory.getClass(type);
452                 } catch (BindingConstructionException e) {
453                         throw new RuntimeBindingConstructionException(e);
454                 }
455     }
456
457     /**
458      * Creates a bean class 
459      * @param type
460      * @return class
461      */
462     public static Class<?> getBeanClass( Datatype type ) throws BindingConstructionException {
463         BindingRequest br = typeClassFactory.getClass(type);
464         return br.getClazz();
465     }
466
467     /**
468      * Create binding from datatype that instantiates java classes. 
469      * RecordTypes are Beans, UnionTypes are Classes with @Union annotation, 
470      * ArrayTypes are []. 
471      * 
472      * @param type
473      * @return class
474      */
475     public static Binding getBeanBinding( Datatype type ) throws RuntimeBindingConstructionException {
476                 try {
477                 BindingRequest br = typeClassFactory.getClass(type);
478                 return getBinding( br );
479                 } catch (BindingConstructionException e) {
480                         throw new RuntimeBindingConstructionException(e);
481                 }
482     }
483     
484     /**
485      * Get a default array binding for a given component type binding.
486      * Returns a primitive array type binding for primitive types and an
487      * ObjectArrayBinding for others.
488      * 
489      * @param componentBinding  A binding for a component type
490      * @return  A binding for the array type
491      */
492     public static Binding getArrayBinding(Binding componentBinding) {
493         return getBinding(new ArrayType(componentBinding.type()));
494     }
495
496         /**
497          * Get serializer that follows Databoard serialization spec.
498          *  
499          * @param binding
500          * @return serializer
501          * @throws SerializerConstructionException
502          */
503         public static Serializer getSerializer(Binding binding) throws SerializerConstructionException {
504                 return serializationFactory.getSerializer(binding);
505         }
506
507         /**
508          * Get serializer that follows Databoard serialization spec.
509          * 
510          * @param binding
511          * @return serializer
512          * @throws RuntimeSerializerConstructionException
513          */
514         public static Serializer getSerializerUnchecked(Binding binding) throws RuntimeSerializerConstructionException {
515                 return serializationFactory.getSerializerUnchecked(binding);
516         }
517
518         /**
519          * Get serializer that follows Databoard serialization spec.
520          *  
521          * @param clazz
522          * @return serializer
523          * @throws SerializerConstructionException
524          */
525         public static Serializer getSerializer(Class<?> clazz) throws SerializerConstructionException {
526                 try {
527                         Binding binding = getBinding(clazz);
528                         return serializationFactory.getSerializer(binding);
529                 } catch (BindingConstructionException e) {
530                         throw new SerializerConstructionException( e );
531                 }
532         }
533
534         /**
535          * Get serializer that follows Databoard serialization spec.
536          * 
537          * @param clazz
538          * @return serializer serializer
539          * @throws RuntimeSerializerConstructionException
540          */
541         public static Serializer getSerializerUnchecked(Class<?> clazz) throws RuntimeSerializerConstructionException {
542                 try {
543                         Binding binding = getBinding(clazz);
544                         return serializationFactory.getSerializerUnchecked(binding);
545                 } catch (BindingConstructionException e) {
546                         throw new RuntimeSerializerConstructionException( new SerializerConstructionException(e) );
547                 }
548         }       
549     /**
550      * Create an adapter that adapts two bindings of the same
551      * data type.
552      * 
553      * @param domain binding of the source instance
554      * @param range binding of the result instance
555      * @return result adapter
556      * @throws AdapterConstructionException 
557      */
558     public static Adapter getAdapter(Binding domain, Binding range)
559     throws AdapterConstructionException
560     {
561         return adapterFactory.getAdapter(domain, range, false, false);
562     }
563     
564     /**
565      * Create an adapter that adapts between two bindings of the same
566      * data type.
567      * 
568      * @param domain binding of the source instance
569      * @param range binding of the result instance
570      * @return result adapter
571      * @throws AdapterConstructionException 
572      */
573     public static Adapter getAdapterUnchecked(Binding domain, Binding range)
574     throws RuntimeAdapterConstructionException
575     {
576         try {
577                         return adapterFactory.getAdapter(domain, range, false, false);
578                 } catch (AdapterConstructionException e) {
579                         throw new RuntimeAdapterConstructionException(e);
580                 }
581     }
582
583     /**
584      * Create a type adapter that adapts instances from one Datatype to 
585      * another. Type Adapter does the following conversions: 
586      * 
587      *    o Number Types, e.g. long -> double
588      *    o Unit Types, e.g. mph -> km/h
589      *    o Record Types, for each field of the range, there must be equal in domain 
590      *    o Union Types, for each tag type of the domain, there must be equal in range
591      * 
592      * {@link AdaptException} is thrown at runtime, if number conversion is not 
593      * posible, e.g. converting value 500 from Integer to Byte.
594      * Note, there is also a possibility of precision loss, in many conversions
595      * e.g. from double to int.
596      * 
597      * @param domain binding of the source instance
598      * @param range binding of the result instance
599      * @return adapter
600      * @throws AdapterConstructionException 
601      */
602     public static Adapter getTypeAdapter(Binding domain, Binding range)
603     throws AdapterConstructionException
604     {
605         return adapterFactory.getAdapter(domain, range, true, false);
606     }
607
608     /**
609      * Create a type adapter that adapts instances from one DataType to 
610      * another. Type Adapter does the following conversions: 
611      * 
612      *    o Number Types, e.g. long -> double
613      *    o Unit Types, e.g. mph -> km/h
614      *    o Record Types, for each field of the range, there must be equal in domain 
615      *    o Union Types, for each tag type of the domain, there must be equal in range
616      * 
617      * {@link AdaptException} is thrown at runtime, if number values are 
618      * not compatible, e.g. converting value 500 from Long to Byte.
619      * Note, there is also a possibility of precision loss, e.g. when 
620      * converting from double to int.
621      * 
622      * @param domain binding of the source instance
623      * @param range binding of the result instance
624      * @return result adapter
625      * @throws AdapterConstructionException 
626      */
627     public static Adapter getTypeAdapterUnchecked(Binding domain, Binding range)
628     {
629         try {
630                         return adapterFactory.getAdapter(domain, range, true, false);
631                 } catch (AdapterConstructionException e) {
632                         throw new RuntimeAdapterConstructionException(e);
633                 }
634     }
635     
636     /**
637      * Adapt a value of one type to another. 
638      * 
639      * @param value
640      * @param domain
641      * @param range
642      * @return adapted value
643      * @throws AdapterConstructionException
644      * @throws AdaptException
645      */
646     public static Object adapt(Object value, Binding domain, Binding range)
647     throws AdaptException
648     {
649         try {
650                 if (domain.equals(range)) {
651                                 return value;
652                 }
653                 else if (range instanceof VariantBinding) {
654                     if (domain instanceof VariantBinding) {
655                         // Copy variant contents directly
656                         Binding contentBinding = ((VariantBinding)domain).getContentBinding( value );
657                         Object content = ((VariantBinding)domain).getContent( value ); 
658                         return ((VariantBinding)range).create( contentBinding, content );
659                     }
660                     else {
661                                 // Default to just wrapping the value (avoid adapter construction to save memory)
662                                 return ((VariantBinding)range).create(domain, value);
663                     }
664                 }
665                 else if (domain instanceof VariantBinding) {
666                     return adapt(((VariantBinding)domain).getContent( value ), ((VariantBinding)domain).getContentBinding( value ), range );
667                 }
668                 else {
669                     return adapterFactory.getAdapter(domain, range, true, false).adapt(value);
670                 }
671                 } catch (AdapterConstructionException | BindingException e) {
672                         throw new AdaptException(e);
673                 }
674     }
675     
676     /**
677      * Adapt a value of one type to another. Exceptions are run-time. Use this
678      * if it safe to assume the conversion will be successful.
679      * 
680      * @param value
681      * @param domain
682      * @param range
683      * @return adapted value
684      * @throws AdapterConstructionException
685      * @throws AdaptException
686      */
687     public static Object adaptUnchecked(Object value, Binding domain, Binding range)
688     throws RuntimeAdapterConstructionException, RuntimeAdaptException
689     {
690         try {
691                 if (domain==range) {
692                                 return value;
693                 }
694                 if (range instanceof VariantBinding && !(domain instanceof VariantBinding)) {
695                         // Default to just wrapping the value (avoid adapter construction to save memory)
696                         return ((VariantBinding)range).create(domain, value);
697                 }
698                 return  adapterFactory.getAdapter(domain, range, true, false).adaptUnchecked(value);
699                 } catch (RuntimeAdapterConstructionException e) {
700                         throw new RuntimeAdaptException(new AdaptException(e.getCause()));
701                 } catch (AdapterConstructionException e) {
702                         throw new RuntimeAdaptException(new AdaptException(e));
703                 } catch (BindingException e) {
704                         throw new RuntimeAdaptException(new AdaptException(e));
705                 }
706     }    
707     
708     /**
709      * Adapt and clone a value instance to another type. Immutable 
710      * bindings may return the argument as is, others return a cloned copy.  
711      * 
712      * @param value
713      * @param domain
714      * @param range
715      * @return adapted value
716      * @throws AdapterConstructionException
717      * @throws AdaptException
718      */
719     public static Object clone(Object value, Binding domain, Binding range)
720     throws AdaptException
721     {
722         try {
723                 if (domain==range) {
724                         if (domain.isImmutable()) {
725                                 return value;
726                         } else { 
727                                 return domain.clone(value);
728                         }
729                 }
730                         return adapterFactory.getAdapter(domain, range, true, true).adapt(value);
731                 } catch (AdapterConstructionException e) {
732                         throw new AdaptException(e);
733                 }
734     }
735     
736
737     /**
738      * Clone a value of one binding to another. Bindings that handle immutable values
739      * may return the same instance, others will guarantee a complete copy.
740      * 
741      * This method throws only runtime exceptions. Use this if it is safe to assume
742      * that the conversion will be successful.
743      * 
744      * @param value
745      * @param domain
746      * @param range
747      * @return adapted value
748      * @throws AdapterConstructionException
749      * @throws AdaptException
750      */
751     public static Object cloneUnchecked(Object value, Binding domain, Binding range)
752     throws RuntimeAdapterConstructionException, RuntimeAdaptException
753     {
754         try {
755                         return adapterFactory.getAdapter(domain, range, true, true).adapt(value);
756                 } catch (AdaptException e) {
757                         throw new RuntimeAdaptException(e);
758                 } catch (RuntimeAdapterConstructionException e) {
759                         throw new RuntimeAdaptException(new AdaptException(e.getCause()));
760                 } catch (AdapterConstructionException e) {
761                         throw new RuntimeAdaptException(new AdaptException(e));
762                 }
763     }
764     
765     /**
766      * Compares two data values for order. Returns a negative integer,
767      * zero, or a positive integer if, the first argument precedes/lesser than 
768      * the second, is equal to, or successor/greater than the second.<p>
769      * 
770      * DataTypes of b1 and b2 are not equal, then data types are compared. <p>
771      *
772      * The comparison function is defined at 
773      * http://dev.simantics.org/index.php/Org.simantics.datatype_Manual#CompareTo_and_Equals <p>
774      * 
775      * Note, comparing 2 different number types will not result a value comparison.
776      * Instead values have the following type precedence ByteType, IntegerType, LongType,
777      * FloatType, and the highest DoubleType. <p>
778      *
779      * @param b1 Binding of o1 
780      * @param o1 the first object to be compared.
781      * @param b2 Binding of o2
782      * @param o2 the second object to be compared.
783      * @return a negative integer, zero, or a positive integer as the
784      *         first argument is less than, equal to, or greater than the
785      *         second.
786      * @throws BindingException if object cannot be handled by a binding
787      */
788     public static int compare(Binding b1, Object o1, Binding b2, Object o2) 
789     throws BindingException
790     {
791         return DataValueUtil.compare(b1, o1, b2, o2);
792     }
793     
794     /**
795      * Compare two data values for equality. <p>
796      * 
797      * Note, comparing 2 different number types will not result a value comparison.
798      * Instead values have the following type precedence ByteType, IntegerType, LongType,
799      * FloatType, and the highest DoubleType. <p>
800      * 
801      * @param b1
802      * @param o1
803      * @param b2
804      * @param o2
805      * @return true if equal
806      * @throws BindingException
807      */
808     public static boolean equals(Binding b1, Object o1, Binding b2, Object o2)
809     throws BindingException
810     {
811         return DataValueUtil.equals(b1, o1, b2, o2);
812     }
813     
814     public static Comparator<Object> createComparator(final Binding b1, final Binding b2)
815     {
816         return DataValueUtil.createComparator(b1, b2);
817     }
818     
819     /**
820      * Print the content of an object as a structure.
821      * Utility function for debug purposes.
822      * 
823      * @param o
824      * @return content
825      */
826     public static String toString(Object o) {
827         try {
828                         Binding b = Bindings.getBinding( o.getClass() );
829                         return b.printValueDefinition(o, true);
830                 } catch (BindingConstructionException e) {
831                         return "<error "+e.getClass().getName()+" "+e.getMessage()+">";
832                 } catch (IOException e) {
833                         return "<error "+e.getClass().getName()+" "+e.getMessage()+">";
834                 } catch (BindingException e) {
835                         return "<error "+e.getClass().getName()+" "+e.getMessage()+">";
836                 }
837     }
838     
839     static {
840         STRING         = new StringBindingDefault( Datatypes.STRING );  
841         INTEGER        = new IntegerBindingDefault( Datatypes.INTEGER );        
842         BOOLEAN        = new BooleanBindingDefault( Datatypes.BOOLEAN );        
843         BYTE           = new ByteBindingDefault( Datatypes.BYTE );      
844         LONG           = new LongBindingDefault( Datatypes.LONG );      
845         DOUBLE         = new DoubleBindingDefault( Datatypes.DOUBLE );  
846         FLOAT          = new FloatBindingDefault( Datatypes.FLOAT );    
847         VOID           = VoidBinding.VOID_BINDING;
848         BOOLEAN_ARRAY  = new BooleanArrayBinding( Datatypes.BOOLEAN_ARRAY, BOOLEAN );   
849         BYTE_ARRAY     = new ByteArrayBinding( Datatypes.BYTE_ARRAY, BYTE );
850         INT_ARRAY      = new IntArrayBinding( Datatypes.INTEGER_ARRAY, INTEGER );
851         LONG_ARRAY     = new LongArrayBinding( Datatypes.LONG_ARRAY, LONG );
852         FLOAT_ARRAY    = new FloatArrayBinding( Datatypes.FLOAT_ARRAY, FLOAT );
853         DOUBLE_ARRAY   = new DoubleArrayBinding( Datatypes.DOUBLE_ARRAY, DOUBLE );
854         STRING_ARRAY   = new StringArrayBinding( Datatypes.STRING_ARRAY, STRING );
855         UNSIGNED_INTEGER         = new UnsignedIntegerBinding.Immutable( Datatypes.INTEGER );
856         UNSIGNED_BYTE            = new UnsignedByteBinding.Immutable( Datatypes.BYTE );
857         UNSIGNED_LONG            = new UnsignedLongBinding.Immutable( Datatypes.LONG );
858         MUTABLE_STRING           = new MutableStringBinding( Datatypes.STRING );
859         MUTABLE_INTEGER          = new MutableIntegerBinding( Datatypes.INTEGER );
860         MUTABLE_BOOLEAN          = new MutableBooleanBinding( Datatypes.BOOLEAN );
861         MUTABLE_BYTE             = new MutableByteBinding( Datatypes.BYTE );
862         MUTABLE_LONG             = new MutableLongBinding( Datatypes.LONG );
863         MUTABLE_FLOAT            = new MutableFloatBinding( Datatypes.FLOAT );
864         MUTABLE_DOUBLE           = new MutableDoubleBinding( Datatypes.DOUBLE );
865         MUTABLE_UNSIGNED_INTEGER = new UnsignedIntegerBinding.Mutable( Datatypes.INTEGER );
866         MUTABLE_UNSIGNED_BYTE    = new UnsignedByteBinding.Mutable( Datatypes.BYTE );
867         MUTABLE_UNSIGNED_LONG    = new UnsignedLongBinding.Mutable( Datatypes.LONG );
868         
869         databoard = new Databoard();
870         
871         mutableBindingRepository = databoard.mutableBindingRepository;
872         defaultBindingRepository = databoard.defaultBindingRepository;
873         bindingRepository = databoard.bindingRepository;
874         serializerRepository = databoard.serializerRepository;
875         mutableBindingFactory = databoard.mutableBindingFactory;
876         defaultBindingFactory = databoard.defaultBindingFactory;
877         classBindingFactory = databoard.classBindingFactory;
878         serializationFactory = databoard.serializationFactory;
879         adapterFactory = databoard.adapterFactory;
880         typeClassFactory = databoard.typeClassFactory;
881         
882         BEAN                             = databoard.BEAN;
883         VARIANT                          = databoard.VARIANT;
884         MUTABLE_VARIANT          = new MutableVariantBinding( classBindingFactory, adapterFactory );
885         STR_VARIANT                      = databoard.STR_VARIANT;
886         OBJECT                           = databoard.OBJECT;
887         
888         databoard.initialize();
889         
890         DATATYPE = getBindingUnchecked(Datatype.class);
891         /**
892          * {@link Datatype} class has annotations but it can be considered a "class
893          * request" as it is a fundamental building block of Databoard and it has a
894          * fixed structure. Therefore {@link BindingRepository#classMap} is allowed
895          * to contain a cached Datatype.class -> Binding mapping.
896          */
897         bindingRepository.registerClassMapping(Datatype.class, DATATYPE);
898     }
899
900 }
901