1 /*******************************************************************************
2 * Copyright (c) 2019 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 * Semantum Oy - gitlab #313
12 *******************************************************************************/
13 package org.simantics.databoard.binding.reflection;
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;
32 import java.util.concurrent.CopyOnWriteArrayList;
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;
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.
104 * @author Toni Kalajainen
106 public class ClassBindingFactory {
109 * Map of failed constructions.
111 Map<BindingRequest, BindingConstructionException> failures = new HashMap<BindingRequest, BindingConstructionException>();
114 * Map that contains in incomplete constructions.
116 Map<BindingRequest, Binding> inprogress = new HashMap<BindingRequest, Binding>();
119 * Repository where successful constructions are placed.
121 BindingRepository repository;
123 List<BindingProvider> subFactories = new CopyOnWriteArrayList<BindingProvider>();
125 /** Asm based binding factory for Classes, this is used if Asm library is available, else null */
126 RecordBindingProvider asmClassBindingFactory;
128 /** Reflection based binding factory for Classes, this has poorer performance than asm factory */
129 RecordBindingProvider refClassBindingFactory;
131 /** Factory for creating default bindings for data types. */
132 TypeBindingFactory defaultBindingFactory;
135 * Construct a new reflection binding factory
137 public ClassBindingFactory() {
139 this.repository = new BindingRepository();
140 this.defaultBindingFactory = new DefaultBindingFactory();
144 * Construct a new reflection binding factory that places constructed
145 * bindings into user given repository.
149 public ClassBindingFactory(BindingRepository repository, TypeBindingFactory defaultBindingFactory) {
151 this.repository = repository;
152 this.defaultBindingFactory = defaultBindingFactory;
156 refClassBindingFactory = new ReflectionBindingProvider();
159 Class.forName("org.objectweb.asm.ClassWriter");
161 Class<?> y = Class.forName("org.simantics.databoard.binding.reflection.AsmBindingProvider");
162 Constructor<?> c = y.getConstructor();
163 asmClassBindingFactory = (RecordBindingProvider) c.newInstance();
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) {
175 public void addFactory(BindingProvider factory)
177 if (!subFactories.contains(factory)) {
178 this.subFactories.add(factory);
182 public void removeFactory(BindingProvider factory) {
183 subFactories.remove(factory);
186 public BindingRepository getRepository() {
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.
197 * inprogress.put(request, notCompletelyConstructedBinding);
198 * Binding componentBinding = construct( componentRequest );
199 * notCompletelyConstructedBinding.setChild( componentBinding );
200 * inprogress.remove(request);
202 * try-finally is not needed.
206 * @throws BindingConstructionException
207 * @throws RangeException
209 @SuppressWarnings("unchecked")
210 protected Binding doConstruct(BindingRequest request) throws BindingConstructionException, RangeException
213 if(request.hasAnnotation(Optional.class))
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);
224 repository.put(request, binding);
232 Range range = request.getAnnotation(Range.class);
233 Unit unit = request.getAnnotation(Unit.class);
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;
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() );
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);
254 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
256 repository.put(request, binding);
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;
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() );
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);
279 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
281 repository.put(request, binding);
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;
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() );
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);
304 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
306 repository.put(request, binding);
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;
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() );
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);
325 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
327 repository.put(request, binding);
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;
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() );
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);
347 repository.put(request, binding);
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());
357 repository.put(request, binding);
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;
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);
377 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
379 repository.put(request, binding);
386 for (BindingProvider factory : subFactories) {
387 Binding result = factory.provideBinding(this, request);
388 if (result == null) continue;
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();
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]);
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");
416 componentRequest = new BindingRequest(componentClass, componentAnnotations);
419 inprogress.put(request, binding);
420 binding.componentBinding = construct( componentRequest );
421 inprogress.remove(request);
424 type.componentType = binding.componentBinding.type();
426 // Multi-dimensional Array. Apply range to sub-array-types
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;
439 if ( result instanceof MapBinding ) {
440 MapBinding binding = (MapBinding) result;
442 Arguments argumentsAnnotation = request.getAnnotation(Arguments.class);
443 Annotation[] componentAnnotations = request.dropAnnotations( 2 );
444 Class<?>[] arguments = argumentsAnnotation != null ? argumentsAnnotation.value() : null;
446 BindingRequest keyRequest = null;
447 BindingRequest valueRequest = null;
449 Binding keyBinding = null;
450 Binding valueBinding = null;
452 if (binding.getKeyBinding() != null) {
453 keyBinding = binding.getKeyBinding();
455 else if (request.componentBindings != null) {
456 keyBinding = request.componentBindings[0];
458 else if (request.componentRequests != null) {
459 keyRequest = request.componentRequests[0];
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");
467 keyRequest = new BindingRequest(keyClass, componentAnnotations);
470 if (binding.getValueBinding() != null) {
471 valueBinding = binding.getValueBinding();
473 else if (request.componentBindings != null) {
474 valueBinding = request.componentBindings[1];
476 else if (request.componentRequests != null) {
477 valueRequest = request.componentRequests[1];
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");
485 valueRequest = new BindingRequest(valueClass, componentAnnotations);
488 inprogress.put(request, result);
489 if (keyRequest!=null) {
490 keyBinding = construct( keyRequest );
492 if (valueRequest!=null) {
493 valueBinding = construct( valueRequest );
495 inprogress.remove(request);
497 MapType type = binding.type();
498 type.keyType = keyBinding.type();
499 type.valueType = valueBinding.type();
500 binding.setKeyBinding( keyBinding );
501 binding.setValueBinding( valueBinding );
512 // Its complete, store to repository
513 repository.put(request, result);
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));
527 EnumClassBinding binding = new EnumClassBinding(type, (Class<Enum<?>>) request.getClazz() );
528 repository.put(request, binding);
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);
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();
553 inprogress.remove(request);
554 repository.put(request, binding);
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 );
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);
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 */);
584 Identifier idAnnotation = componentRequest.getAnnotation( Identifier.class );
585 if ( idAnnotation!=null ) {
586 componentRequest = componentRequest.withAnnotations(componentRequest.dropAnnotations(1, idAnnotation));
587 identifierIndices.add( i );
589 Binding componentBinding = componentBindings[i] = construct( componentRequest );
590 c.type = componentBinding.type();
592 type.setIdentifiers(identifierIndices);
593 inprogress.remove(request);
594 repository.put(request, binding);
599 public Binding construct(BindingRequest request)
600 throws BindingConstructionException
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);
606 // Start construction
608 Binding binding = doConstruct(request);
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;
619 } catch (RangeException e) {
620 inprogress.remove( request );
621 BindingConstructionException bce = new BindingConstructionException( e );
622 failures.put(request, bce);
624 } catch (BindingConstructionException e) {
625 inprogress.remove( request );
626 failures.put(request, e);
628 } catch (Throwable t) {
629 BindingConstructionException bce = new BindingConstructionException( t );
630 inprogress.remove( request );
631 failures.put(request, bce);
636 boolean isComplete(Binding binding, IdentityHashSet<Binding> checked) {
637 for (Binding b : inprogress.values())
638 if (b == binding) return false;
640 if (checked.contains(binding)) return true;
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))
654 * Get a binding to a Java Class. Type details can be modified with annotations.
655 * Please see the package org.simantics.databoard.annotations.
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>
663 * Note, results are stored with strong reference into the repository assigned
664 * to this factory.<p>
666 * @see ClassBindingFactory
669 * @throws BindingConstructionException
671 @SuppressWarnings("unchecked")
672 public <T extends Binding> T getBinding(Class<?> clazz)
673 throws BindingConstructionException
675 return (T) construct( new BindingRequest(clazz) );
679 * Get a binding to a Java Class. Use this method to acquire class
680 * parameters for a generics class. <p>
684 * Binding binding = getBinding(Map.class, String.class, Integer.class);
685 * Map<String, Integer> map = (Map<String, Integer>) binding.createDefault();
689 * Binding d = getBinding(List.class, Integer.class);
690 * List<Integer> list = (List<Integer>) d.createRandom(5);
694 * Binding d = getBinding(List.class, List.class, Integer.class);
695 * List<List<Integer>> list = (List<List<Integer>>) d.createRandom(5);
697 * @see ClassBindingFactory
700 * @throws BindingConstructionException
702 @SuppressWarnings("unchecked")
703 public <T extends Binding> T getBinding(Class<?> clazz, Class<?>...parameters)
704 throws BindingConstructionException
706 Arguments args = new ArgumentImpl(parameters);
707 BindingRequest request = new BindingRequest( clazz, args );
708 return (T) construct( request );
711 public Binding getBinding(BindingRequest request) throws BindingConstructionException {
712 return construct(request);
715 public Binding getBindingUnchecked(BindingRequest request) throws RuntimeBindingConstructionException {
717 return construct(request);
718 } catch (BindingConstructionException e) {
719 throw new RuntimeBindingConstructionException(e);
723 static Class<?>[] NO_CLASSES = new Class<?>[0];
725 public static Annotation[] getFieldAnnotations(Field field)
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 );
733 return getTypeAnnotations(annotations, fieldClass, parameterClasses);
736 public static Annotation[] getMethodAnnotations(Method method)
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 );
744 return getTypeAnnotations(annotations, valueClass, parameterClasses);
747 private static Annotation[] getTypeAnnotations(Annotation[] annotations, Class<?> fieldClass,
748 Class<?>[] parameterClasses) {
749 if (Set.class.isAssignableFrom(fieldClass) && parameterClasses!=null &¶meterClasses.length==1) {
750 Annotation[] a2 = new Annotation[annotations.length+1];
751 System.arraycopy(annotations, 0, a2, 0, annotations.length);
753 Class<?> keyType = parameterClasses[0];
754 a2[annotations.length] = new ArgumentImpl(keyType);
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);
761 Class<?> keyType = parameterClasses[0];
762 Class<?> valueType = parameterClasses[1];
763 a2[annotations.length] = new ArgumentImpl(keyType, valueType);
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);
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);
784 static void getTypes(Type type, Collection<Class<?>> result)
786 if ( type instanceof Class ) {
787 result.add( (Class<?>) type );
789 if ( type instanceof ParameterizedType ) {
790 ParameterizedType p = (ParameterizedType) type;
791 getTypes( p.getRawType(), result );
792 for ( Type x : p.getActualTypeArguments() ) getTypes(x, result);
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 );
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 );
805 throw new RuntimeException( type.getClass()+ " is not implemented" );
809 * Get all actual parameters types, incl. classes and generic types
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();