1 package org.simantics.databoard.binding.reflection;
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;
19 import java.util.concurrent.CopyOnWriteArrayList;
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;
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.
91 * @author Toni Kalajainen
93 public class ClassBindingFactory {
96 * Map of failed constructions.
98 Map<BindingRequest, BindingConstructionException> failures = new HashMap<BindingRequest, BindingConstructionException>();
101 * Map that contains in incomplete constructions.
103 Map<BindingRequest, Binding> inprogress = new HashMap<BindingRequest, Binding>();
106 * Repository where successful constructions are placed.
108 BindingRepository repository;
110 List<BindingProvider> subFactories = new CopyOnWriteArrayList<BindingProvider>();
112 /** Asm based binding factory for Classes, this is used if Asm library is available, else null */
113 RecordBindingProvider asmClassBindingFactory;
115 /** Reflection based binding factory for Classes, this has poorer performance than asm factory */
116 RecordBindingProvider refClassBindingFactory;
118 /** Factory for creating default bindings for data types. */
119 TypeBindingFactory defaultBindingFactory;
122 * Construct a new reflection binding factory
124 public ClassBindingFactory() {
126 this.repository = new BindingRepository();
127 this.defaultBindingFactory = new DefaultBindingFactory();
131 * Construct a new reflection binding factory that places constructed
132 * bindings into user given repository.
136 public ClassBindingFactory(BindingRepository repository, TypeBindingFactory defaultBindingFactory) {
138 this.repository = repository;
139 this.defaultBindingFactory = defaultBindingFactory;
143 refClassBindingFactory = new ReflectionBindingProvider();
146 Class.forName("org.objectweb.asm.ClassWriter");
148 Class<?> y = Class.forName("org.simantics.databoard.binding.reflection.AsmBindingProvider");
149 Constructor<?> c = y.getConstructor();
150 asmClassBindingFactory = (RecordBindingProvider) c.newInstance();
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) {
162 public void addFactory(BindingProvider factory)
164 if (!subFactories.contains(factory)) {
165 this.subFactories.add(factory);
169 public void removeFactory(BindingProvider factory) {
170 subFactories.remove(factory);
173 public BindingRepository getRepository() {
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.
184 * inprogress.put(request, notCompletelyConstructedBinding);
185 * Binding componentBinding = construct( componentRequest );
186 * notCompletelyConstructedBinding.setChild( componentBinding );
187 * inprogress.remove(request);
189 * try-finally is not needed.
193 * @throws BindingConstructionException
194 * @throws RangeException
196 @SuppressWarnings("unchecked")
197 protected Binding doConstruct(BindingRequest request) throws BindingConstructionException, RangeException
200 if(request.hasAnnotation(Optional.class))
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);
219 Range range = request.getAnnotation(Range.class);
220 Unit unit = request.getAnnotation(Unit.class);
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;
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() );
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);
241 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
243 repository.put(request, binding);
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;
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() );
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);
266 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
268 repository.put(request, binding);
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;
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() );
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);
291 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
293 repository.put(request, binding);
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;
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() );
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);
312 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
314 repository.put(request, binding);
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;
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() );
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);
334 repository.put(request, binding);
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());
344 repository.put(request, binding);
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;
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);
364 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
366 repository.put(request, binding);
373 for (BindingProvider factory : subFactories) {
374 Binding result = factory.provideBinding(this, request);
375 if (result == null) continue;
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();
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]);
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");
403 componentRequest = new BindingRequest(componentClass, componentAnnotations);
406 inprogress.put(request, binding);
407 binding.componentBinding = construct( componentRequest );
408 inprogress.remove(request);
411 type.componentType = binding.componentBinding.type();
413 // Multi-dimensional Array. Apply range to sub-array-types
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;
426 if ( result instanceof MapBinding ) {
427 MapBinding binding = (MapBinding) result;
429 Arguments argumentsAnnotation = request.getAnnotation(Arguments.class);
430 Annotation[] componentAnnotations = request.dropAnnotations( 2 );
431 Class<?>[] arguments = argumentsAnnotation != null ? argumentsAnnotation.value() : null;
433 BindingRequest keyRequest = null;
434 BindingRequest valueRequest = null;
436 Binding keyBinding = null;
437 Binding valueBinding = null;
439 if (binding.getKeyBinding() != null) {
440 keyBinding = binding.getKeyBinding();
442 else if (request.componentBindings != null) {
443 keyBinding = request.componentBindings[0];
445 else if (request.componentRequests != null) {
446 keyRequest = request.componentRequests[0];
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");
454 keyRequest = new BindingRequest(keyClass, componentAnnotations);
457 if (binding.getValueBinding() != null) {
458 valueBinding = binding.getValueBinding();
460 else if (request.componentBindings != null) {
461 valueBinding = request.componentBindings[1];
463 else if (request.componentRequests != null) {
464 valueRequest = request.componentRequests[1];
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");
472 valueRequest = new BindingRequest(valueClass, componentAnnotations);
475 inprogress.put(request, result);
476 if (keyRequest!=null) {
477 keyBinding = construct( keyRequest );
479 if (valueRequest!=null) {
480 valueBinding = construct( valueRequest );
482 inprogress.remove(request);
484 MapType type = binding.type();
485 type.keyType = keyBinding.type();
486 type.valueType = valueBinding.type();
487 binding.setKeyBinding( keyBinding );
488 binding.setValueBinding( valueBinding );
499 // Its complete, store to repository
500 repository.put(request, result);
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));
514 EnumClassBinding binding = new EnumClassBinding(type, (Class<Enum<?>>) request.getClazz() );
515 repository.put(request, binding);
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);
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();
540 inprogress.remove(request);
541 repository.put(request, binding);
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 );
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);
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 */);
571 Identifier idAnnotation = componentRequest.getAnnotation( Identifier.class );
572 if ( idAnnotation!=null ) {
573 componentRequest.dropAnnotations(1, idAnnotation);
574 identifierIndices.add( i );
576 Binding componentBinding = componentBindings[i] = construct( componentRequest );
577 c.type = componentBinding.type();
579 type.setIdentifiers(identifierIndices);
580 inprogress.remove(request);
581 repository.put(request, binding);
586 public Binding construct(BindingRequest request)
587 throws BindingConstructionException
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);
593 // Start construction
595 Binding binding = doConstruct(request);
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;
605 repository.put(request, binding);
608 } catch (RangeException e) {
609 inprogress.remove( request );
610 BindingConstructionException bce = new BindingConstructionException( e );
611 failures.put(request, bce);
613 } catch (BindingConstructionException e) {
614 inprogress.remove( request );
615 failures.put(request, e);
617 } catch (Throwable t) {
618 BindingConstructionException bce = new BindingConstructionException( t );
619 inprogress.remove( request );
620 failures.put(request, bce);
625 boolean isComplete(Binding binding, IdentityHashSet<Binding> checked) {
626 for (Binding b : inprogress.values())
627 if (b == binding) return false;
629 if (checked.contains(binding)) return true;
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))
643 * Get a binding to a Java Class. Type details can be modified with annotations.
644 * Please see the package org.simantics.databoard.annotations.
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>
652 * Note, results are stored with strong reference into the repository assigned
653 * to this factory.<p>
655 * @see ClassBindingFactory
658 * @throws BindingConstructionException
660 @SuppressWarnings("unchecked")
661 public <T extends Binding> T getBinding(Class<?> clazz)
662 throws BindingConstructionException
664 return (T) construct( new BindingRequest(clazz) );
668 * Get a binding to a Java Class. Use this method to acquire class
669 * parameters for a generics class. <p>
673 * Binding binding = getBinding(Map.class, String.class, Integer.class);
674 * Map<String, Integer> map = (Map<String, Integer>) binding.createDefault();
678 * Binding d = getBinding(List.class, Integer.class);
679 * List<Integer> list = (List<Integer>) d.createRandom(5);
683 * Binding d = getBinding(List.class, List.class, Integer.class);
684 * List<List<Integer>> list = (List<List<Integer>>) d.createRandom(5);
686 * @see ClassBindingFactory
689 * @throws BindingConstructionException
691 @SuppressWarnings("unchecked")
692 public <T extends Binding> T getBinding(Class<?> clazz, Class<?>...parameters)
693 throws BindingConstructionException
695 Arguments args = new ArgumentImpl(parameters);
696 BindingRequest request = new BindingRequest( clazz, args );
697 return (T) construct( request );
700 public Binding getBinding(BindingRequest request) throws BindingConstructionException {
701 return construct(request);
704 public Binding getBindingUnchecked(BindingRequest request) throws RuntimeBindingConstructionException {
706 return construct(request);
707 } catch (BindingConstructionException e) {
708 throw new RuntimeBindingConstructionException(e);
712 static Class<?>[] NO_CLASSES = new Class<?>[0];
714 public static Annotation[] getFieldAnnotations(Field field)
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 );
722 if (Set.class.isAssignableFrom(fieldClass) && parameterClasses!=null &¶meterClasses.length==1) {
723 Annotation[] a2 = new Annotation[annotations.length+1];
724 System.arraycopy(annotations, 0, a2, 0, annotations.length);
726 Class<?> keyType = parameterClasses[0];
727 a2[annotations.length] = new ArgumentImpl(keyType);
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);
734 Class<?> keyType = parameterClasses[0];
735 Class<?> valueType = parameterClasses[1];
736 a2[annotations.length] = new ArgumentImpl(keyType, valueType);
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);
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);
757 static void getTypes(Type type, Collection<Class<?>> result)
759 if ( type instanceof Class ) {
760 result.add( (Class<?>) type );
762 if ( type instanceof ParameterizedType ) {
763 ParameterizedType p = (ParameterizedType) type;
764 getTypes( p.getRawType(), result );
765 for ( Type x : p.getActualTypeArguments() ) getTypes(x, result);
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 );
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 );
778 throw new RuntimeException( type.getClass()+ " is not implemented" );
782 * Get all actual parameters types, incl. classes and generic types
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();