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