]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/ClassBindingFactory.java
8d29ad70c24ac4c8896293b22ac82817b1c617f3
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / reflection / ClassBindingFactory.java
1 package org.simantics.databoard.binding.reflection;
2
3 import java.lang.annotation.Annotation;
4 import java.lang.reflect.Array;
5 import java.lang.reflect.Constructor;
6 import java.lang.reflect.Field;
7 import java.lang.reflect.GenericArrayType;
8 import java.lang.reflect.InvocationTargetException;
9 import java.lang.reflect.Modifier;
10 import java.lang.reflect.ParameterizedType;
11 import java.lang.reflect.Type;
12 import java.lang.reflect.TypeVariable;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.concurrent.CopyOnWriteArrayList;
20
21 import org.simantics.databoard.Bindings;
22 import org.simantics.databoard.annotations.ArgumentImpl;
23 import org.simantics.databoard.annotations.Arguments;
24 import org.simantics.databoard.annotations.Identifier;
25 import org.simantics.databoard.annotations.Length;
26 import org.simantics.databoard.annotations.MIMEType;
27 import org.simantics.databoard.annotations.Name;
28 import org.simantics.databoard.annotations.Optional;
29 import org.simantics.databoard.annotations.Pattern;
30 import org.simantics.databoard.annotations.Range;
31 import org.simantics.databoard.annotations.Referable;
32 import org.simantics.databoard.annotations.Union;
33 import org.simantics.databoard.annotations.Unit;
34 import org.simantics.databoard.binding.ArrayBinding;
35 import org.simantics.databoard.binding.Binding;
36 import org.simantics.databoard.binding.MapBinding;
37 import org.simantics.databoard.binding.OptionalBinding;
38 import org.simantics.databoard.binding.RecordBinding;
39 import org.simantics.databoard.binding.error.BindingConstructionException;
40 import org.simantics.databoard.binding.error.RuntimeBindingConstructionException;
41 import org.simantics.databoard.binding.factory.BindingRepository;
42 import org.simantics.databoard.binding.factory.DefaultBindingFactory;
43 import org.simantics.databoard.binding.factory.TypeBindingFactory;
44 import org.simantics.databoard.binding.impl.ByteBindingDefault;
45 import org.simantics.databoard.binding.impl.DoubleBindingDefault;
46 import org.simantics.databoard.binding.impl.FloatBindingDefault;
47 import org.simantics.databoard.binding.impl.IntegerBindingDefault;
48 import org.simantics.databoard.binding.impl.LongBindingDefault;
49 import org.simantics.databoard.binding.impl.OptionalBindingDefault;
50 import org.simantics.databoard.binding.impl.StringBindingDefault;
51 import org.simantics.databoard.binding.impl.UnsignedByteBinding;
52 import org.simantics.databoard.binding.impl.UnsignedIntegerBinding;
53 import org.simantics.databoard.binding.impl.UnsignedLongBinding;
54 import org.simantics.databoard.binding.mutable.MutableByteBinding;
55 import org.simantics.databoard.binding.mutable.MutableDoubleBinding;
56 import org.simantics.databoard.binding.mutable.MutableFloatBinding;
57 import org.simantics.databoard.binding.mutable.MutableIntegerBinding;
58 import org.simantics.databoard.binding.mutable.MutableLongBinding;
59 import org.simantics.databoard.binding.mutable.MutableStringBinding;
60 import org.simantics.databoard.primitives.MutableBoolean;
61 import org.simantics.databoard.primitives.MutableByte;
62 import org.simantics.databoard.primitives.MutableDouble;
63 import org.simantics.databoard.primitives.MutableFloat;
64 import org.simantics.databoard.primitives.MutableInteger;
65 import org.simantics.databoard.primitives.MutableLong;
66 import org.simantics.databoard.primitives.MutableString;
67 import org.simantics.databoard.primitives.UnsignedByte;
68 import org.simantics.databoard.primitives.UnsignedInteger;
69 import org.simantics.databoard.primitives.UnsignedLong;
70 import org.simantics.databoard.type.ArrayType;
71 import org.simantics.databoard.type.ByteType;
72 import org.simantics.databoard.type.Component;
73 import org.simantics.databoard.type.DoubleType;
74 import org.simantics.databoard.type.FloatType;
75 import org.simantics.databoard.type.IntegerType;
76 import org.simantics.databoard.type.LongType;
77 import org.simantics.databoard.type.MapType;
78 import org.simantics.databoard.type.OptionalType;
79 import org.simantics.databoard.type.RecordType;
80 import org.simantics.databoard.type.StringType;
81 import org.simantics.databoard.type.UnionType;
82 import org.simantics.databoard.util.ArrayUtils;
83 import org.simantics.databoard.util.IdentityHashSet;
84 import org.simantics.databoard.util.RangeException;
85
86 /**
87  * Type Factory constructs data types from reflection requests.
88  * Successfully constructed types are placed in the repository that was given 
89  * at construction time.
90  *  
91  * @author Toni Kalajainen
92  */
93 public class ClassBindingFactory {
94         
95         /**
96          * Map of failed constructions. 
97          */
98         Map<BindingRequest, BindingConstructionException> failures = new HashMap<BindingRequest, BindingConstructionException>();
99         
100         /**
101          * Map that contains in incomplete constructions.
102          */
103         Map<BindingRequest, Binding> inprogress = new HashMap<BindingRequest, Binding>();
104         
105         /**
106          * Repository where successful constructions are placed. 
107          */
108         BindingRepository repository;   
109         
110         List<BindingProvider> subFactories = new CopyOnWriteArrayList<BindingProvider>();
111
112         /** Asm based binding factory for Classes, this is used if Asm library is available, else null */
113         RecordBindingProvider asmClassBindingFactory;
114         
115         /** Reflection based binding factory for Classes, this has poorer performance than asm factory */
116         RecordBindingProvider refClassBindingFactory;
117
118         /** Factory for creating default bindings for data types. */
119         TypeBindingFactory defaultBindingFactory;
120         
121         /**
122          * Construct a new reflection binding factory 
123          */
124         public ClassBindingFactory() {
125                 init();
126                 this.repository = new BindingRepository();
127                 this.defaultBindingFactory = new DefaultBindingFactory();
128         }
129         
130         /**
131          * Construct a new reflection binding factory that places constructed 
132          * bindings into user given repository.
133          * 
134          * @param repository
135          */
136         public ClassBindingFactory(BindingRepository repository, TypeBindingFactory defaultBindingFactory) {
137                 init();
138                 this.repository = repository;
139                 this.defaultBindingFactory = defaultBindingFactory;
140         }
141         
142         void init() {
143                 refClassBindingFactory = new ReflectionBindingProvider();
144                 try {
145                         // Check ASM Exists
146                         Class.forName("org.objectweb.asm.ClassWriter");
147                         // Create factory
148                         Class<?> y = Class.forName("org.simantics.databoard.binding.reflection.AsmBindingProvider");
149                         Constructor<?> c = y.getConstructor();
150                         asmClassBindingFactory = (RecordBindingProvider) c.newInstance();
151                         return;
152                 } catch (ClassNotFoundException e) {
153                 } catch (InstantiationException e) {
154                 } catch (IllegalAccessException e) {
155                 } catch (IllegalArgumentException e) {
156                 } catch (InvocationTargetException e) {
157                 } catch (SecurityException e) {
158                 } catch (NoSuchMethodException e) {
159                 }
160         }
161         
162         public void addFactory(BindingProvider factory) 
163         {
164                 if (!subFactories.contains(factory)) {
165                         this.subFactories.add(factory);
166                 }
167         }
168         
169         public void removeFactory(BindingProvider factory) {
170                 subFactories.remove(factory);
171         }
172         
173         public BindingRepository getRepository() {
174                 return repository;
175         }
176         
177         /**
178          * Constructs a binding to comply to class request.
179          * This is the method sub-classes implement. 
180          * The implementation should use the inprogress -map for construction of 
181          * bindings that have component types.
182          * 
183          *  e.g. 
184          *   inprogress.put(request, notCompletelyConstructedBinding);
185          *   Binding componentBinding = construct( componentRequest );
186          *   notCompletelyConstructedBinding.setChild( componentBinding );
187          *   inprogress.remove(request);
188          *   
189          * try-finally is not needed.
190          * 
191          * @param request
192          * @return
193          * @throws BindingConstructionException
194          * @throws RangeException
195          */
196         @SuppressWarnings("unchecked")
197     protected Binding doConstruct(BindingRequest request) throws BindingConstructionException, RangeException
198         {
199                 // Optional
200         if(request.hasAnnotation(Optional.class))
201         {               
202                 Optional optional = request.getAnnotation(Optional.class); 
203                 Annotation[] newAnnotations = ArrayUtils.dropElements(request.annotations, optional);
204                 BindingRequest componentRequest = new BindingRequest(request.getClazz(), newAnnotations);
205                 OptionalType type = new OptionalType();
206                 OptionalBinding binding = new OptionalBindingDefault(type, null);
207                         inprogress.put(request, binding);
208                         binding.componentBinding = construct( componentRequest );
209                         type.componentType = binding.componentBinding.type();
210                         inprogress.remove(request);
211                         
212                         return binding;
213         }
214
215             
216         
217         // Primitive
218                 {               
219                         Range range = request.getAnnotation(Range.class);
220                         Unit unit = request.getAnnotation(Unit.class);
221                         
222                         if (request.getClazz() == Integer.class || request.getClazz() == int.class || request.getClazz() == MutableInteger.class || UnsignedInteger.class.isAssignableFrom(request.getClazz())) {
223                                 Binding binding = null; 
224                                 if (range==null && unit==null) {
225                                         if (request.getClazz() == int.class) binding = Bindings.INTEGER;
226                                         if (request.getClazz() == Integer.class) binding = Bindings.INTEGER;
227                                         if (request.getClazz() == MutableInteger.class) binding = Bindings.MUTABLE_INTEGER;
228                                         if (request.getClazz() == UnsignedInteger.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_INTEGER;
229                                         if (request.getClazz() == UnsignedInteger.Immutable.class) binding = Bindings.UNSIGNED_INTEGER;
230                                 } else {
231                                         IntegerType type = new IntegerType();
232                                         type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
233                                         type.setUnit( unit==null ? null : unit.value() );
234
235                                         if (request.getClazz() == int.class) binding = new IntegerBindingDefault(type);
236                                         if (request.getClazz() == Integer.class) binding = new IntegerBindingDefault(type);
237                                         if (request.getClazz() == MutableInteger.class) binding = new MutableIntegerBinding(type);
238                                         if (request.getClazz() == UnsignedInteger.Mutable.class) binding = new UnsignedIntegerBinding.Mutable(type);
239                                         if (request.getClazz() == UnsignedInteger.Immutable.class) binding = new UnsignedIntegerBinding.Immutable(type);
240                                 }
241                                 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
242                                 
243                                 repository.put(request, binding);
244                                 return binding;
245                         }
246                                 
247                         if (request.getClazz() == Byte.class || request.getClazz() == byte.class || request.getClazz() == MutableByte.class || UnsignedByte.class.isAssignableFrom(request.getClazz())) {
248                                 Binding binding = null; 
249                                 if (range==null && unit==null) {
250                                         if (request.getClazz() == byte.class) binding = Bindings.BYTE;
251                                         if (request.getClazz() == Byte.class) binding = Bindings.BYTE;
252                                         if (request.getClazz() == MutableByte.class) binding = Bindings.MUTABLE_BYTE;
253                                         if (request.getClazz() == UnsignedByte.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_BYTE;
254                                         if (request.getClazz() == UnsignedByte.Immutable.class) binding = Bindings.UNSIGNED_BYTE;
255                                 } else {
256                                         ByteType type = new ByteType();
257                                         type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
258                                         type.setUnit( unit==null ? null : unit.value() );
259
260                                         if (request.getClazz() == byte.class) binding = new ByteBindingDefault(type);
261                                         if (request.getClazz() == Byte.class) binding = new ByteBindingDefault(type);
262                                         if (request.getClazz() == MutableByte.class) binding = new MutableByteBinding(type);
263                                         if (request.getClazz() == UnsignedByte.Mutable.class) binding = new UnsignedByteBinding.Mutable(type);
264                                         if (request.getClazz() == UnsignedByte.Immutable.class) binding = new UnsignedByteBinding.Immutable(type);
265                                 }
266                                 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
267                                 
268                                 repository.put(request, binding);                       
269                                 return binding;
270                         }                       
271                         
272                         if (request.getClazz() == Long.class || request.getClazz() == long.class || request.getClazz() == MutableLong.class || UnsignedLong.class.isAssignableFrom(request.getClazz())) {
273                                 Binding binding = null; 
274                                 if (range==null && unit==null) {
275                                         if (request.getClazz() == long.class) binding = Bindings.LONG;
276                                         if (request.getClazz() == Long.class) binding = Bindings.LONG;
277                                         if (request.getClazz() == MutableLong.class) binding = Bindings.MUTABLE_LONG;
278                                         if (request.getClazz() == UnsignedLong.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_LONG;
279                                         if (request.getClazz() == UnsignedLong.Immutable.class) binding = Bindings.UNSIGNED_LONG;
280                                 } else {
281                                         LongType type = new LongType();
282                                         type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
283                                         type.setUnit( unit==null ? null : unit.value() );
284
285                                         if (request.getClazz() == long.class) binding = new LongBindingDefault(type);
286                                         if (request.getClazz() == Long.class) binding = new LongBindingDefault(type);
287                                         if (request.getClazz() == MutableLong.class) binding = new MutableLongBinding(type);
288                                         if (request.getClazz() == UnsignedLong.Mutable.class) binding = new UnsignedLongBinding.Mutable(type);
289                                         if (request.getClazz() == UnsignedLong.Immutable.class) binding = new UnsignedLongBinding.Immutable(type);
290                                 }
291                                 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
292                                 
293                                 repository.put(request, binding);                       
294                                 return binding;
295                         }
296                         
297                         if (request.getClazz() == Float.class || request.getClazz() == float.class || request.getClazz() == MutableFloat.class) {
298                                 Binding binding = null; 
299                                 if (range==null && unit==null) {
300                                         if (request.getClazz() == float.class) binding = Bindings.FLOAT;
301                                         if (request.getClazz() == Float.class) binding = Bindings.FLOAT;
302                                         if (request.getClazz() == MutableFloat.class) binding = Bindings.MUTABLE_FLOAT;
303                                 } else {
304                                         FloatType type = new FloatType();
305                                         type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
306                                         type.setUnit( unit==null ? null : unit.value() );
307
308                                         if (request.getClazz() == float.class) binding = new FloatBindingDefault(type);
309                                         if (request.getClazz() == Float.class) binding = new FloatBindingDefault(type);
310                                         if (request.getClazz() == MutableFloat.class) binding = new MutableFloatBinding(type);
311                                 }
312                                 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
313                                 
314                                 repository.put(request, binding);                       
315                                 return binding;
316                         }
317                         
318                         if (request.getClazz() == Double.class || request.getClazz() == double.class || request.getClazz() == MutableDouble.class) {
319                                 Binding binding = null; 
320                                 if (range==null && unit==null) {
321                                         if (request.getClazz() == double.class) binding = Bindings.DOUBLE;
322                                         if (request.getClazz() == Double.class) binding = Bindings.DOUBLE;
323                                         if (request.getClazz() == MutableDouble.class) binding = Bindings.MUTABLE_DOUBLE;
324                                 } else {
325                                         DoubleType type = new DoubleType();
326                                         type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
327                                         type.setUnit( unit==null ? null : unit.value() );
328
329                                         if (request.getClazz() == double.class) binding = new DoubleBindingDefault(type);
330                                         if (request.getClazz() == Double.class) binding = new DoubleBindingDefault(type);
331                                         if (request.getClazz() == MutableDouble.class) binding = new MutableDoubleBinding(type);
332                                 }
333                                 
334                                 repository.put(request, binding);                       
335                                 return binding;
336                         }
337
338                         if (request.getClazz() == Boolean.class || request.getClazz() == boolean.class || request.getClazz() == MutableBoolean.class) {
339                                 Binding binding = null;
340                         if (request.getClazz() == Boolean.class || request.getClazz() == boolean.class) binding = Bindings.BOOLEAN;
341                         if (request.getClazz() == MutableBoolean.class) binding = Bindings.MUTABLE_BOOLEAN;             
342                                 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
343                                 
344                                 repository.put(request, binding);                       
345                                 return binding;
346                         }
347
348                         if (request.getClazz() == String.class || request.getClazz() == MutableString.class) {
349                                 Length length = request.getAnnotation(Length.class);
350                                 MIMEType mimeType = request.getAnnotation(MIMEType.class);
351                                 Pattern pattern = request.getAnnotation(Pattern.class);
352                                 Binding binding = null;
353                                 if (length==null && mimeType==null && pattern==null) {
354                                         if (request.getClazz() == String.class) binding = Bindings.STRING;
355                                         if (request.getClazz() == MutableString.class) binding = Bindings.MUTABLE_STRING;
356                                         } else {
357                                                 StringType type = new StringType();
358                                                 type.setLength( length==null ? null : org.simantics.databoard.util.Range.valueOf( length.value()[0] ) );
359                                                 type.setMimeType( mimeType==null ? null : mimeType.value() );
360                                                 type.setPattern( pattern==null ? null : pattern.value() );                                      
361                                         if (request.getClazz() == String.class) binding = new StringBindingDefault(type);
362                                         if (request.getClazz() == MutableString.class) binding = new MutableStringBinding(type);
363                                         }
364                                 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
365                                 
366                     repository.put(request, binding);                        
367                                 return binding;
368                         }
369                 }
370
371                 
372         // Custom factories
373         for (BindingProvider factory : subFactories) {
374                 Binding result = factory.provideBinding(this, request);                 
375                 if (result == null) continue;
376
377                 /// Array
378                 // If the result was an arraybinding, complete the composite binding
379                 if (result instanceof ArrayBinding) {
380                         ArrayBinding binding = (ArrayBinding) result;
381                         ArrayType type = binding.type();
382                 Length lengthAnnotation = request.getAnnotation(Length.class);
383                 Arguments argumentsAnnotation = request.getAnnotation(Arguments.class);
384                 Annotation[] componentAnnotations = request.dropAnnotations(1, lengthAnnotation);                               
385                 Class<?> componentClass = argumentsAnnotation!=null ? argumentsAnnotation.value()[0] : request.getClazz().getComponentType();
386
387                 org.simantics.databoard.util.Range[] lengths = null;
388                 if (lengthAnnotation!=null) {
389                         String[] strs = lengthAnnotation.value();
390                         lengths = new org.simantics.databoard.util.Range[strs.length];
391                     for (int i=0; i<strs.length; i++)
392                         lengths[i] = org.simantics.databoard.util.Range.valueOf(strs[i]);                               
393                 }
394                 
395                 if ( binding.componentBinding==null && request.componentBindings!=null ) binding.componentBinding = request.componentBindings[0];
396                 if ( binding.componentBinding == null) {
397                         BindingRequest componentRequest = request.componentRequests != null ? request.componentRequests[0] : null;
398                                 if (componentRequest==null) {
399                                         if (componentClass==null) {
400                                                 componentClass = Object.class;
401                                         // throw new BindingConstructionException("Cannot determine array component type");
402                                         }
403                                         componentRequest = new BindingRequest(componentClass, componentAnnotations);                    
404                                 }
405                                 
406                                 inprogress.put(request, binding);
407                                 binding.componentBinding = construct( componentRequest );
408                                 inprogress.remove(request);
409                         }
410                 
411                         type.componentType = binding.componentBinding.type();
412                 
413                 // Multi-dimensional Array. Apply range to sub-array-types
414                 ArrayType t = type;
415                 if (lengths!=null) {
416                     for (int i=0; i<lengths.length; i++) {
417                         t.setLength( lengths[i] );
418                         if (i<lengths.length-1)
419                             t = (ArrayType) t.componentType;
420                     }
421                 }                   
422                 }
423                 
424                 /// Map
425             // Map Type
426             if ( result instanceof MapBinding ) {
427                 MapBinding binding = (MapBinding) result;
428                 
429                         Arguments argumentsAnnotation = request.getAnnotation(Arguments.class);
430                         Annotation[] componentAnnotations = request.dropAnnotations( 2 );
431                 Class<?>[] arguments = argumentsAnnotation != null ? argumentsAnnotation.value() : null;
432
433                 BindingRequest keyRequest = null;
434                 BindingRequest valueRequest = null;
435                 
436                 Binding keyBinding = null;
437                 Binding valueBinding = null;
438                         
439                         if (binding.getKeyBinding() != null) {
440                             keyBinding = binding.getKeyBinding();
441                         }
442                         else if (request.componentBindings != null) {
443                                 keyBinding = request.componentBindings[0];
444                         }
445                         else if (request.componentRequests != null) {
446                                 keyRequest = request.componentRequests[0];
447                         }
448                         else {
449                                 Class<?> keyClass = arguments != null && arguments.length >= 1 ? arguments[0] : null;
450                     if (keyClass==null) {
451                         keyClass = Object.class;
452                         //throw new BindingConstructionException("Cannot determine key class, use @Arguments annotation");
453                     }
454                     keyRequest = new BindingRequest(keyClass, componentAnnotations);
455                         }
456                         
457                         if (binding.getValueBinding() != null) {
458                     valueBinding = binding.getValueBinding();
459                         }
460                         else if (request.componentBindings != null) {
461                                 valueBinding = request.componentBindings[1];
462                         }
463                         else if (request.componentRequests != null) {
464                                 valueRequest = request.componentRequests[1];
465                         }
466                         else {
467                                 Class<?> valueClass = arguments != null && arguments.length >= 2 ? arguments[1] : null;
468                     if (valueClass==null) {
469                         valueClass = Object.class;
470                         //throw new BindingConstructionException("Cannot determine value class, use @Arguments annotation");
471                     }
472                     valueRequest = new BindingRequest(valueClass, componentAnnotations);
473                         }
474                         
475                         inprogress.put(request, result);
476                         if (keyRequest!=null) {
477                                 keyBinding = construct( keyRequest );
478                         }
479                         if (valueRequest!=null) {
480                                 valueBinding = construct( valueRequest );
481                         }
482                 inprogress.remove(request);
483                 
484                 MapType type = binding.type();
485                 type.keyType = keyBinding.type();
486                 type.valueType = valueBinding.type();
487                         binding.setKeyBinding( keyBinding );
488                         binding.setValueBinding( valueBinding );
489             }
490                 
491                 /// Optional
492                 
493                 
494                 /// Union
495                 
496                 
497                 /// Record
498                 
499                 // Its complete, store to repository
500                         repository.put(request, result);                        
501                 return result;
502         }
503                 
504                 
505         
506         if (request.getClazz().isEnum()) {
507             Enum<?>[] enums = (Enum[]) request.getClazz().getEnumConstants();
508             UnionType type = new UnionType();               
509             type.components = new Component[enums.length];
510             for (int i=0; i<enums.length; i++) {
511                 String name = enums[i].name();
512                 type.components[i] = new Component(name, new RecordType(false));                
513             }                               
514             EnumClassBinding binding = new EnumClassBinding(type, (Class<Enum<?>>) request.getClazz() );                            
515             repository.put(request, binding);
516             return binding;
517         }               
518         
519         // Union type
520         if(request.hasAnnotation(Union.class)) {
521                 Union union = request.getAnnotation(Union.class);                       
522                 UnionType type = new UnionType();
523                         UnionClassBinding binding = new UnionClassBinding(type);
524             Class<?>[] cases = union.value();
525                         int count = cases.length; 
526             Binding[] componentBindings = new Binding[count];
527             type.components = new Component[ count ];
528             binding.componentClasses = new Class<?>[ count ];
529             binding.setComponentBindings(componentBindings);
530             
531                         inprogress.put(request, binding);                   
532             for(int i=0;i<count;++i) {
533                 binding.componentClasses[i]= cases[i];
534                 Component component = type.components[i] = new Component(cases[i].getSimpleName(), null /* updated later*/);
535                 BindingRequest componentRequest = new BindingRequest( cases[i] );
536                 Binding componentBinding = componentBindings[i] = construct( componentRequest );
537                 component.type = componentBinding.type();                       
538             }
539             
540                         inprogress.remove(request);
541                         repository.put(request, binding);
542             return binding;
543         }
544         
545         // Record type
546         {
547                 RecordType type = new RecordType();
548                 ClassInfo ci = ClassInfo.getInfo( request.getClazz() );
549                 boolean publicClass = Modifier.isPublic( request.getClazz().getModifiers() ); 
550                 boolean useAsmBinding = asmClassBindingFactory!=null && publicClass;
551                 RecordBindingProvider f = useAsmBinding ? asmClassBindingFactory : refClassBindingFactory;
552                 RecordBinding binding = f.provideRecordBinding( request.getClazz(), type);
553                 int count = ci.fields.length;
554                 Binding[] componentBindings = binding.getComponentBindings();                   
555             Component[] components = new Component[ count ];
556             type.setReferable( request.getAnnotation(Referable.class) != null );
557             type.setComponents( components );
558
559                         inprogress.put(request, binding);
560                         List<Integer> identifierIndices = new ArrayList<Integer>(1);
561             for(int i=0;i<type.getComponentCount();++i) {
562                 Field field = ci.fields[i];
563                 Annotation[] annotations = getFieldAnnotations(field);
564                 Class<?> fieldClass = field.getType(); 
565                 BindingRequest componentRequest = new BindingRequest(fieldClass, annotations);
566                 
567                 Name nameAnnotation = componentRequest.getAnnotation( Name.class );
568                 String fieldName = nameAnnotation!=null ? nameAnnotation.value() : field.getName();
569                 Component c = components[i] = new Component(fieldName, null /* updated later */);
570                 
571                 Identifier idAnnotation = componentRequest.getAnnotation( Identifier.class );
572                 if ( idAnnotation!=null ) {
573                         componentRequest.dropAnnotations(1, idAnnotation);
574                         identifierIndices.add( i );
575                 }
576                 Binding componentBinding = componentBindings[i] = construct( componentRequest );
577                 c.type = componentBinding.type();
578             }
579             type.setIdentifiers(identifierIndices);
580                         inprogress.remove(request);
581                         repository.put(request, binding);
582             return binding;
583         }
584         }
585         
586         public Binding construct(BindingRequest request) 
587         throws BindingConstructionException
588         {
589                 if (failures.containsKey(request)) throw failures.get(request);
590                 if (inprogress.containsKey(request)) return inprogress.get(request);
591                 if (repository.containsRequest(request)) return repository.get(request);
592                 
593                 // Start construction
594                 try {                   
595                         Binding binding = doConstruct(request);
596                         
597                         // Avoid creating duplicate binding instances
598                         // Only check bindings that are complete
599                         if (inprogress.isEmpty() || isComplete(binding, new IdentityHashSet<Binding>())) {
600                                 Binding defaultBinding = defaultBindingFactory.getBinding(binding.type());
601                                 if (defaultBinding != null && defaultBinding.equals(binding))
602                                         binding = defaultBinding;
603                         }
604                         
605                         repository.put(request, binding);
606                         
607                         return binding;
608                 } catch (RangeException e) {
609                         inprogress.remove( request );
610                         BindingConstructionException bce = new BindingConstructionException( e ); 
611                         failures.put(request, bce);
612                         throw bce;
613                 } catch (BindingConstructionException e) {
614                         inprogress.remove( request );
615                         failures.put(request, e);
616                         throw e;
617                 } catch (Throwable t) {
618                         BindingConstructionException bce = new BindingConstructionException( t );
619                         inprogress.remove( request );
620                         failures.put(request, bce);
621                         throw bce;
622                 }
623         }
624         
625         boolean isComplete(Binding binding, IdentityHashSet<Binding> checked) {
626                 for (Binding b : inprogress.values())
627                         if (b == binding) return false;
628                 
629                 if (checked.contains(binding)) return true;
630                 
631                 if (binding.getComponentCount() > 0) {
632                         checked.add(binding);
633                         for (int i = 0; i < binding.getComponentCount(); i++) {
634                                 if (!isComplete(binding.getComponentBinding(i), checked))
635                                         return false;
636                         }
637                 }
638                 
639                 return true;
640         }
641
642         /**
643          * Get a binding to a Java Class. Type details can be modified with annotations.
644          * Please see the package org.simantics.databoard.annotations. 
645          * <p>
646          *  
647          * The whether the result binding is a completely mutable or not depends on the
648          * provided classes. For instance, fields such as Boolean, Integer, Long
649          * are not mutable, instead MutableBoolean, MutableInteger and MutableLong are.
650          * The length of Object[] is not mutable, but length of List<Object> is. <p>
651          * 
652          * Note, results are stored with strong reference into the repository assigned
653          * to this factory.<p> 
654          * 
655          * @see ClassBindingFactory  
656          * @param clazz
657          * @return binding
658          * @throws BindingConstructionException
659          */
660     @SuppressWarnings("unchecked")
661         public <T extends Binding> T getBinding(Class<?> clazz)
662     throws BindingConstructionException
663     {
664         return (T) construct( new BindingRequest(clazz) );
665     }
666     
667         /**
668          * Get a binding to a Java Class. Use this method to acquire class 
669          * parameters for a generics class. <p>
670          * 
671          * Example 1: 
672          * 
673          *    Binding binding = getBinding(Map.class, String.class, Integer.class);
674          *    Map<String, Integer> map = (Map<String, Integer>) binding.createDefault();
675          *    
676          * Example 2:
677          *    
678      *  Binding d = getBinding(List.class, Integer.class);
679          *  List<Integer> list = (List<Integer>) d.createRandom(5);
680          *    
681          * Example 3:
682          *    
683      *  Binding d = getBinding(List.class, List.class, Integer.class);
684          *  List<List<Integer>> list = (List<List<Integer>>) d.createRandom(5);
685          * 
686          * @see ClassBindingFactory  
687          * @param clazz
688          * @return binding
689          * @throws BindingConstructionException
690          */
691     @SuppressWarnings("unchecked")
692         public <T extends Binding> T getBinding(Class<?> clazz, Class<?>...parameters)
693     throws BindingConstructionException
694     {
695         Arguments args = new ArgumentImpl(parameters);
696         BindingRequest request = new BindingRequest( clazz, args );
697         return (T) construct( request );
698     }    
699         
700         public Binding getBinding(BindingRequest request) throws BindingConstructionException {         
701                 return construct(request);
702         }
703         
704         public Binding getBindingUnchecked(BindingRequest request) throws RuntimeBindingConstructionException {
705                 try {
706                         return construct(request);
707                 } catch (BindingConstructionException e) {
708                         throw new RuntimeBindingConstructionException(e);
709                 }
710         }       
711         
712         static Class<?>[] NO_CLASSES = new Class<?>[0];
713         
714     public static Annotation[] getFieldAnnotations(Field field) 
715     {
716         Annotation[] annotations = field.getAnnotations().clone();
717         ArrayList<Class<?>> list = new ArrayList<Class<?>>();
718         getTypes( field.getGenericType(), list );
719         Class<?> fieldClass = list.remove(0);
720         Class<?>[] parameterClasses = list.isEmpty() ? NO_CLASSES : list.toArray( NO_CLASSES );
721         
722         if (Set.class.isAssignableFrom(fieldClass) && parameterClasses!=null &&parameterClasses.length==1) {
723                 Annotation[] a2 = new Annotation[annotations.length+1];
724                 System.arraycopy(annotations, 0, a2, 0, annotations.length);
725                 
726                 Class<?> keyType = parameterClasses[0];
727                 a2[annotations.length] = new ArgumentImpl(keyType);                                             
728                 annotations = a2;
729         }       
730         if (Map.class.isAssignableFrom(fieldClass) && parameterClasses!=null && parameterClasses.length==2) {
731                 Annotation[] a2 = new Annotation[annotations.length+1];
732                 System.arraycopy(annotations, 0, a2, 0, annotations.length);
733                 
734                 Class<?> keyType = parameterClasses[0];
735                 Class<?> valueType = parameterClasses[1];
736                 a2[annotations.length] = new ArgumentImpl(keyType, valueType);                                          
737                 annotations = a2;
738         }       
739         if (List.class.isAssignableFrom(fieldClass) && parameterClasses!=null && parameterClasses.length==1) {
740                 Annotation[] a2 = new Annotation[annotations.length+1];
741                 System.arraycopy(annotations, 0, a2, 0, annotations.length);
742                 Class<?> componentType = parameterClasses[0];
743                 a2[annotations.length] = new ArgumentImpl(componentType);
744                 annotations = a2;
745         }       
746         
747         if (parameterClasses!=null && parameterClasses.length>0) {
748                 Annotation[] a2 = new Annotation[annotations.length+1];
749                 System.arraycopy(annotations, 0, a2, 0, annotations.length);
750                 a2[annotations.length] = new ArgumentImpl(parameterClasses);
751                 annotations = a2;
752         }
753         
754         return annotations;
755     }
756     
757     static void getTypes(Type type, Collection<Class<?>> result) 
758     {
759         if ( type instanceof Class ) {
760                 result.add( (Class<?>) type );
761         } else 
762         if ( type instanceof ParameterizedType ) {
763                         ParameterizedType p = (ParameterizedType) type;
764                         getTypes( p.getRawType(), result );
765                 for ( Type x : p.getActualTypeArguments() ) getTypes(x, result);
766         } else
767                 if ( type instanceof GenericArrayType) {
768                         GenericArrayType at = (GenericArrayType) type;
769                         Type componentType = at.getGenericComponentType();
770                         ArrayList<Class<?>> list = new ArrayList<Class<?>>(1);
771                         getTypes( componentType, list );
772                         // To Array class
773                         Object dummy = Array.newInstance(list.get(0), 0);
774                         result.add( dummy.getClass() ); 
775                 } else if ( type instanceof TypeVariable ) {
776                         result.add( Object.class );              
777                 } else
778                 throw new RuntimeException( type.getClass()+ " is not implemented" );
779     }
780     
781     /**
782      * Get all actual parameters types, incl. classes and generic types
783      * 
784      * @param f
785      * @return
786      */
787         static Type[] getParameterTypes(Field f) {
788                 Type t = f.getGenericType();
789                 if (t==null || t instanceof ParameterizedType==false) return new Class[0];              
790                 ParameterizedType p = (ParameterizedType) t;
791                 return p.getActualTypeArguments();
792     }
793     
794
795 }