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.Modifier;
22 import java.lang.reflect.ParameterizedType;
23 import java.lang.reflect.Type;
24 import java.lang.reflect.TypeVariable;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.HashMap;
28 import java.util.List;
31 import java.util.concurrent.CopyOnWriteArrayList;
33 import org.simantics.databoard.Bindings;
34 import org.simantics.databoard.annotations.ArgumentImpl;
35 import org.simantics.databoard.annotations.Arguments;
36 import org.simantics.databoard.annotations.Identifier;
37 import org.simantics.databoard.annotations.Length;
38 import org.simantics.databoard.annotations.MIMEType;
39 import org.simantics.databoard.annotations.Name;
40 import org.simantics.databoard.annotations.Optional;
41 import org.simantics.databoard.annotations.Pattern;
42 import org.simantics.databoard.annotations.Range;
43 import org.simantics.databoard.annotations.Referable;
44 import org.simantics.databoard.annotations.Union;
45 import org.simantics.databoard.annotations.Unit;
46 import org.simantics.databoard.binding.ArrayBinding;
47 import org.simantics.databoard.binding.Binding;
48 import org.simantics.databoard.binding.MapBinding;
49 import org.simantics.databoard.binding.OptionalBinding;
50 import org.simantics.databoard.binding.RecordBinding;
51 import org.simantics.databoard.binding.error.BindingConstructionException;
52 import org.simantics.databoard.binding.error.RuntimeBindingConstructionException;
53 import org.simantics.databoard.binding.factory.BindingRepository;
54 import org.simantics.databoard.binding.factory.DefaultBindingFactory;
55 import org.simantics.databoard.binding.factory.TypeBindingFactory;
56 import org.simantics.databoard.binding.impl.ByteBindingDefault;
57 import org.simantics.databoard.binding.impl.DoubleBindingDefault;
58 import org.simantics.databoard.binding.impl.FloatBindingDefault;
59 import org.simantics.databoard.binding.impl.IntegerBindingDefault;
60 import org.simantics.databoard.binding.impl.LongBindingDefault;
61 import org.simantics.databoard.binding.impl.OptionalBindingDefault;
62 import org.simantics.databoard.binding.impl.StringBindingDefault;
63 import org.simantics.databoard.binding.impl.UnsignedByteBinding;
64 import org.simantics.databoard.binding.impl.UnsignedIntegerBinding;
65 import org.simantics.databoard.binding.impl.UnsignedLongBinding;
66 import org.simantics.databoard.binding.mutable.MutableByteBinding;
67 import org.simantics.databoard.binding.mutable.MutableDoubleBinding;
68 import org.simantics.databoard.binding.mutable.MutableFloatBinding;
69 import org.simantics.databoard.binding.mutable.MutableIntegerBinding;
70 import org.simantics.databoard.binding.mutable.MutableLongBinding;
71 import org.simantics.databoard.binding.mutable.MutableStringBinding;
72 import org.simantics.databoard.primitives.MutableBoolean;
73 import org.simantics.databoard.primitives.MutableByte;
74 import org.simantics.databoard.primitives.MutableDouble;
75 import org.simantics.databoard.primitives.MutableFloat;
76 import org.simantics.databoard.primitives.MutableInteger;
77 import org.simantics.databoard.primitives.MutableLong;
78 import org.simantics.databoard.primitives.MutableString;
79 import org.simantics.databoard.primitives.UnsignedByte;
80 import org.simantics.databoard.primitives.UnsignedInteger;
81 import org.simantics.databoard.primitives.UnsignedLong;
82 import org.simantics.databoard.type.ArrayType;
83 import org.simantics.databoard.type.ByteType;
84 import org.simantics.databoard.type.Component;
85 import org.simantics.databoard.type.DoubleType;
86 import org.simantics.databoard.type.FloatType;
87 import org.simantics.databoard.type.IntegerType;
88 import org.simantics.databoard.type.LongType;
89 import org.simantics.databoard.type.MapType;
90 import org.simantics.databoard.type.OptionalType;
91 import org.simantics.databoard.type.RecordType;
92 import org.simantics.databoard.type.StringType;
93 import org.simantics.databoard.type.UnionType;
94 import org.simantics.databoard.util.ArrayUtils;
95 import org.simantics.databoard.util.IdentityHashSet;
96 import org.simantics.databoard.util.RangeException;
99 * Type Factory constructs data types from reflection requests.
100 * Successfully constructed types are placed in the repository that was given
101 * at construction time.
103 * @author Toni Kalajainen
105 public class ClassBindingFactory {
108 * Map of failed constructions.
110 Map<BindingRequest, BindingConstructionException> failures = new HashMap<BindingRequest, BindingConstructionException>();
113 * Map that contains in incomplete constructions.
115 Map<BindingRequest, Binding> inprogress = new HashMap<BindingRequest, Binding>();
118 * Repository where successful constructions are placed.
120 BindingRepository repository;
122 List<BindingProvider> subFactories = new CopyOnWriteArrayList<BindingProvider>();
124 /** Asm based binding factory for Classes, this is used if Asm library is available, else null */
125 RecordBindingProvider asmClassBindingFactory;
127 /** Reflection based binding factory for Classes, this has poorer performance than asm factory */
128 RecordBindingProvider refClassBindingFactory;
130 /** Factory for creating default bindings for data types. */
131 TypeBindingFactory defaultBindingFactory;
134 * Construct a new reflection binding factory
136 public ClassBindingFactory() {
138 this.repository = new BindingRepository();
139 this.defaultBindingFactory = new DefaultBindingFactory();
143 * Construct a new reflection binding factory that places constructed
144 * bindings into user given repository.
148 public ClassBindingFactory(BindingRepository repository, TypeBindingFactory defaultBindingFactory) {
150 this.repository = repository;
151 this.defaultBindingFactory = defaultBindingFactory;
155 refClassBindingFactory = new ReflectionBindingProvider();
158 Class.forName("org.objectweb.asm.ClassWriter");
160 Class<?> y = Class.forName("org.simantics.databoard.binding.reflection.AsmBindingProvider");
161 Constructor<?> c = y.getConstructor();
162 asmClassBindingFactory = (RecordBindingProvider) c.newInstance();
164 } catch (ClassNotFoundException e) {
165 } catch (InstantiationException e) {
166 } catch (IllegalAccessException e) {
167 } catch (IllegalArgumentException e) {
168 } catch (InvocationTargetException e) {
169 } catch (SecurityException e) {
170 } catch (NoSuchMethodException e) {
174 public void addFactory(BindingProvider factory)
176 if (!subFactories.contains(factory)) {
177 this.subFactories.add(factory);
181 public void removeFactory(BindingProvider factory) {
182 subFactories.remove(factory);
185 public BindingRepository getRepository() {
190 * Constructs a binding to comply to class request.
191 * This is the method sub-classes implement.
192 * The implementation should use the inprogress -map for construction of
193 * bindings that have component types.
196 * inprogress.put(request, notCompletelyConstructedBinding);
197 * Binding componentBinding = construct( componentRequest );
198 * notCompletelyConstructedBinding.setChild( componentBinding );
199 * inprogress.remove(request);
201 * try-finally is not needed.
205 * @throws BindingConstructionException
206 * @throws RangeException
208 @SuppressWarnings("unchecked")
209 protected Binding doConstruct(BindingRequest request) throws BindingConstructionException, RangeException
212 if(request.hasAnnotation(Optional.class))
214 Optional optional = request.getAnnotation(Optional.class);
215 BindingRequest componentRequest = request.withAnnotations(ArrayUtils.dropElements(request.annotations, optional));
216 OptionalType type = new OptionalType();
217 OptionalBinding binding = new OptionalBindingDefault(type, null);
218 inprogress.put(request, binding);
219 binding.componentBinding = construct( componentRequest );
220 type.componentType = binding.componentBinding.type();
221 inprogress.remove(request);
223 repository.put(request, binding);
231 Range range = request.getAnnotation(Range.class);
232 Unit unit = request.getAnnotation(Unit.class);
234 if (request.getClazz() == Integer.class || request.getClazz() == int.class || request.getClazz() == MutableInteger.class || UnsignedInteger.class.isAssignableFrom(request.getClazz())) {
235 Binding binding = null;
236 if (range==null && unit==null) {
237 if (request.getClazz() == int.class) binding = Bindings.INTEGER;
238 if (request.getClazz() == Integer.class) binding = Bindings.INTEGER;
239 if (request.getClazz() == MutableInteger.class) binding = Bindings.MUTABLE_INTEGER;
240 if (request.getClazz() == UnsignedInteger.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_INTEGER;
241 if (request.getClazz() == UnsignedInteger.Immutable.class) binding = Bindings.UNSIGNED_INTEGER;
243 IntegerType type = new IntegerType();
244 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
245 type.setUnit( unit==null ? null : unit.value() );
247 if (request.getClazz() == int.class) binding = new IntegerBindingDefault(type);
248 if (request.getClazz() == Integer.class) binding = new IntegerBindingDefault(type);
249 if (request.getClazz() == MutableInteger.class) binding = new MutableIntegerBinding(type);
250 if (request.getClazz() == UnsignedInteger.Mutable.class) binding = new UnsignedIntegerBinding.Mutable(type);
251 if (request.getClazz() == UnsignedInteger.Immutable.class) binding = new UnsignedIntegerBinding.Immutable(type);
253 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
255 repository.put(request, binding);
259 if (request.getClazz() == Byte.class || request.getClazz() == byte.class || request.getClazz() == MutableByte.class || UnsignedByte.class.isAssignableFrom(request.getClazz())) {
260 Binding binding = null;
261 if (range==null && unit==null) {
262 if (request.getClazz() == byte.class) binding = Bindings.BYTE;
263 if (request.getClazz() == Byte.class) binding = Bindings.BYTE;
264 if (request.getClazz() == MutableByte.class) binding = Bindings.MUTABLE_BYTE;
265 if (request.getClazz() == UnsignedByte.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_BYTE;
266 if (request.getClazz() == UnsignedByte.Immutable.class) binding = Bindings.UNSIGNED_BYTE;
268 ByteType type = new ByteType();
269 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
270 type.setUnit( unit==null ? null : unit.value() );
272 if (request.getClazz() == byte.class) binding = new ByteBindingDefault(type);
273 if (request.getClazz() == Byte.class) binding = new ByteBindingDefault(type);
274 if (request.getClazz() == MutableByte.class) binding = new MutableByteBinding(type);
275 if (request.getClazz() == UnsignedByte.Mutable.class) binding = new UnsignedByteBinding.Mutable(type);
276 if (request.getClazz() == UnsignedByte.Immutable.class) binding = new UnsignedByteBinding.Immutable(type);
278 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
280 repository.put(request, binding);
284 if (request.getClazz() == Long.class || request.getClazz() == long.class || request.getClazz() == MutableLong.class || UnsignedLong.class.isAssignableFrom(request.getClazz())) {
285 Binding binding = null;
286 if (range==null && unit==null) {
287 if (request.getClazz() == long.class) binding = Bindings.LONG;
288 if (request.getClazz() == Long.class) binding = Bindings.LONG;
289 if (request.getClazz() == MutableLong.class) binding = Bindings.MUTABLE_LONG;
290 if (request.getClazz() == UnsignedLong.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_LONG;
291 if (request.getClazz() == UnsignedLong.Immutable.class) binding = Bindings.UNSIGNED_LONG;
293 LongType type = new LongType();
294 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
295 type.setUnit( unit==null ? null : unit.value() );
297 if (request.getClazz() == long.class) binding = new LongBindingDefault(type);
298 if (request.getClazz() == Long.class) binding = new LongBindingDefault(type);
299 if (request.getClazz() == MutableLong.class) binding = new MutableLongBinding(type);
300 if (request.getClazz() == UnsignedLong.Mutable.class) binding = new UnsignedLongBinding.Mutable(type);
301 if (request.getClazz() == UnsignedLong.Immutable.class) binding = new UnsignedLongBinding.Immutable(type);
303 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
305 repository.put(request, binding);
309 if (request.getClazz() == Float.class || request.getClazz() == float.class || request.getClazz() == MutableFloat.class) {
310 Binding binding = null;
311 if (range==null && unit==null) {
312 if (request.getClazz() == float.class) binding = Bindings.FLOAT;
313 if (request.getClazz() == Float.class) binding = Bindings.FLOAT;
314 if (request.getClazz() == MutableFloat.class) binding = Bindings.MUTABLE_FLOAT;
316 FloatType type = new FloatType();
317 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
318 type.setUnit( unit==null ? null : unit.value() );
320 if (request.getClazz() == float.class) binding = new FloatBindingDefault(type);
321 if (request.getClazz() == Float.class) binding = new FloatBindingDefault(type);
322 if (request.getClazz() == MutableFloat.class) binding = new MutableFloatBinding(type);
324 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
326 repository.put(request, binding);
330 if (request.getClazz() == Double.class || request.getClazz() == double.class || request.getClazz() == MutableDouble.class) {
331 Binding binding = null;
332 if (range==null && unit==null) {
333 if (request.getClazz() == double.class) binding = Bindings.DOUBLE;
334 if (request.getClazz() == Double.class) binding = Bindings.DOUBLE;
335 if (request.getClazz() == MutableDouble.class) binding = Bindings.MUTABLE_DOUBLE;
337 DoubleType type = new DoubleType();
338 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
339 type.setUnit( unit==null ? null : unit.value() );
341 if (request.getClazz() == double.class) binding = new DoubleBindingDefault(type);
342 if (request.getClazz() == Double.class) binding = new DoubleBindingDefault(type);
343 if (request.getClazz() == MutableDouble.class) binding = new MutableDoubleBinding(type);
346 repository.put(request, binding);
350 if (request.getClazz() == Boolean.class || request.getClazz() == boolean.class || request.getClazz() == MutableBoolean.class) {
351 Binding binding = null;
352 if (request.getClazz() == Boolean.class || request.getClazz() == boolean.class) binding = Bindings.BOOLEAN;
353 if (request.getClazz() == MutableBoolean.class) binding = Bindings.MUTABLE_BOOLEAN;
354 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
356 repository.put(request, binding);
360 if (request.getClazz() == String.class || request.getClazz() == MutableString.class) {
361 Length length = request.getAnnotation(Length.class);
362 MIMEType mimeType = request.getAnnotation(MIMEType.class);
363 Pattern pattern = request.getAnnotation(Pattern.class);
364 Binding binding = null;
365 if (length==null && mimeType==null && pattern==null) {
366 if (request.getClazz() == String.class) binding = Bindings.STRING;
367 if (request.getClazz() == MutableString.class) binding = Bindings.MUTABLE_STRING;
369 StringType type = new StringType();
370 type.setLength( length==null ? null : org.simantics.databoard.util.Range.valueOf( length.value()[0] ) );
371 type.setMimeType( mimeType==null ? null : mimeType.value() );
372 type.setPattern( pattern==null ? null : pattern.value() );
373 if (request.getClazz() == String.class) binding = new StringBindingDefault(type);
374 if (request.getClazz() == MutableString.class) binding = new MutableStringBinding(type);
376 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
378 repository.put(request, binding);
385 for (BindingProvider factory : subFactories) {
386 Binding result = factory.provideBinding(this, request);
387 if (result == null) continue;
390 // If the result was an arraybinding, complete the composite binding
391 if (result instanceof ArrayBinding) {
392 ArrayBinding binding = (ArrayBinding) result;
393 ArrayType type = binding.type();
394 Length lengthAnnotation = request.getAnnotation(Length.class);
395 Arguments argumentsAnnotation = request.getAnnotation(Arguments.class);
396 Annotation[] componentAnnotations = request.dropAnnotations(1, lengthAnnotation);
397 Class<?> componentClass = argumentsAnnotation!=null ? argumentsAnnotation.value()[0] : request.getClazz().getComponentType();
399 org.simantics.databoard.util.Range[] lengths = null;
400 if (lengthAnnotation!=null) {
401 String[] strs = lengthAnnotation.value();
402 lengths = new org.simantics.databoard.util.Range[strs.length];
403 for (int i=0; i<strs.length; i++)
404 lengths[i] = org.simantics.databoard.util.Range.valueOf(strs[i]);
407 if ( binding.componentBinding==null && request.componentBindings!=null ) binding.componentBinding = request.componentBindings[0];
408 if ( binding.componentBinding == null) {
409 BindingRequest componentRequest = request.componentRequests != null ? request.componentRequests[0] : null;
410 if (componentRequest==null) {
411 if (componentClass==null) {
412 componentClass = Object.class;
413 // throw new BindingConstructionException("Cannot determine array component type");
415 componentRequest = new BindingRequest(componentClass, componentAnnotations);
418 inprogress.put(request, binding);
419 binding.componentBinding = construct( componentRequest );
420 inprogress.remove(request);
423 type.componentType = binding.componentBinding.type();
425 // Multi-dimensional Array. Apply range to sub-array-types
428 for (int i=0; i<lengths.length; i++) {
429 t.setLength( lengths[i] );
430 if (i<lengths.length-1)
431 t = (ArrayType) t.componentType;
438 if ( result instanceof MapBinding ) {
439 MapBinding binding = (MapBinding) result;
441 Arguments argumentsAnnotation = request.getAnnotation(Arguments.class);
442 Annotation[] componentAnnotations = request.dropAnnotations( 2 );
443 Class<?>[] arguments = argumentsAnnotation != null ? argumentsAnnotation.value() : null;
445 BindingRequest keyRequest = null;
446 BindingRequest valueRequest = null;
448 Binding keyBinding = null;
449 Binding valueBinding = null;
451 if (binding.getKeyBinding() != null) {
452 keyBinding = binding.getKeyBinding();
454 else if (request.componentBindings != null) {
455 keyBinding = request.componentBindings[0];
457 else if (request.componentRequests != null) {
458 keyRequest = request.componentRequests[0];
461 Class<?> keyClass = arguments != null && arguments.length >= 1 ? arguments[0] : null;
462 if (keyClass==null) {
463 keyClass = Object.class;
464 //throw new BindingConstructionException("Cannot determine key class, use @Arguments annotation");
466 keyRequest = new BindingRequest(keyClass, componentAnnotations);
469 if (binding.getValueBinding() != null) {
470 valueBinding = binding.getValueBinding();
472 else if (request.componentBindings != null) {
473 valueBinding = request.componentBindings[1];
475 else if (request.componentRequests != null) {
476 valueRequest = request.componentRequests[1];
479 Class<?> valueClass = arguments != null && arguments.length >= 2 ? arguments[1] : null;
480 if (valueClass==null) {
481 valueClass = Object.class;
482 //throw new BindingConstructionException("Cannot determine value class, use @Arguments annotation");
484 valueRequest = new BindingRequest(valueClass, componentAnnotations);
487 inprogress.put(request, result);
488 if (keyRequest!=null) {
489 keyBinding = construct( keyRequest );
491 if (valueRequest!=null) {
492 valueBinding = construct( valueRequest );
494 inprogress.remove(request);
496 MapType type = binding.type();
497 type.keyType = keyBinding.type();
498 type.valueType = valueBinding.type();
499 binding.setKeyBinding( keyBinding );
500 binding.setValueBinding( valueBinding );
511 // Its complete, store to repository
512 repository.put(request, result);
518 if (request.getClazz().isEnum()) {
519 Enum<?>[] enums = (Enum[]) request.getClazz().getEnumConstants();
520 UnionType type = new UnionType();
521 type.components = new Component[enums.length];
522 for (int i=0; i<enums.length; i++) {
523 String name = enums[i].name();
524 type.components[i] = new Component(name, new RecordType(false));
526 EnumClassBinding binding = new EnumClassBinding(type, (Class<Enum<?>>) request.getClazz() );
527 repository.put(request, binding);
532 if(request.hasAnnotation(Union.class)) {
533 Union union = request.getAnnotation(Union.class);
534 UnionType type = new UnionType();
535 UnionClassBinding binding = new UnionClassBinding(type);
536 Class<?>[] cases = union.value();
537 int count = cases.length;
538 Binding[] componentBindings = new Binding[count];
539 type.components = new Component[ count ];
540 binding.componentClasses = new Class<?>[ count ];
541 binding.setComponentBindings(componentBindings);
543 inprogress.put(request, binding);
544 for(int i=0;i<count;++i) {
545 binding.componentClasses[i]= cases[i];
546 Component component = type.components[i] = new Component(cases[i].getSimpleName(), null /* updated later*/);
547 BindingRequest componentRequest = new BindingRequest( cases[i] );
548 Binding componentBinding = componentBindings[i] = construct( componentRequest );
549 component.type = componentBinding.type();
552 inprogress.remove(request);
553 repository.put(request, binding);
559 RecordType type = new RecordType();
560 ClassInfo ci = ClassInfo.getInfo( request.getClazz() );
561 boolean publicClass = Modifier.isPublic( request.getClazz().getModifiers() );
562 boolean useAsmBinding = asmClassBindingFactory!=null && publicClass;
563 RecordBindingProvider f = useAsmBinding ? asmClassBindingFactory : refClassBindingFactory;
564 RecordBinding binding = f.provideRecordBinding( request.getClazz(), type);
565 int count = ci.fields.length;
566 Binding[] componentBindings = binding.getComponentBindings();
567 Component[] components = new Component[ count ];
568 type.setReferable( request.getAnnotation(Referable.class) != null );
569 type.setComponents( components );
571 inprogress.put(request, binding);
572 List<Integer> identifierIndices = new ArrayList<Integer>(1);
573 for(int i=0;i<type.getComponentCount();++i) {
574 Field field = ci.fields[i];
575 Annotation[] annotations = getFieldAnnotations(field);
576 Class<?> fieldClass = field.getType();
577 BindingRequest componentRequest = new BindingRequest(fieldClass, annotations);
579 Name nameAnnotation = componentRequest.getAnnotation( Name.class );
580 String fieldName = nameAnnotation!=null ? nameAnnotation.value() : field.getName();
581 Component c = components[i] = new Component(fieldName, null /* updated later */);
583 Identifier idAnnotation = componentRequest.getAnnotation( Identifier.class );
584 if ( idAnnotation!=null ) {
585 componentRequest = componentRequest.withAnnotations(componentRequest.dropAnnotations(1, idAnnotation));
586 identifierIndices.add( i );
588 Binding componentBinding = componentBindings[i] = construct( componentRequest );
589 c.type = componentBinding.type();
591 type.setIdentifiers(identifierIndices);
592 inprogress.remove(request);
593 repository.put(request, binding);
598 public Binding construct(BindingRequest request)
599 throws BindingConstructionException
601 if (failures.containsKey(request)) throw failures.get(request);
602 if (inprogress.containsKey(request)) return inprogress.get(request);
603 if (repository.containsRequest(request)) return repository.get(request);
605 // Start construction
607 Binding binding = doConstruct(request);
609 // Avoid creating duplicate binding instances
610 // Only check bindings that are complete
611 if (inprogress.isEmpty() || isComplete(binding, new IdentityHashSet<Binding>())) {
612 Binding defaultBinding = defaultBindingFactory.getBinding(binding.type());
613 if (defaultBinding != null && defaultBinding.equals(binding))
614 binding = defaultBinding;
618 } catch (RangeException e) {
619 inprogress.remove( request );
620 BindingConstructionException bce = new BindingConstructionException( e );
621 failures.put(request, bce);
623 } catch (BindingConstructionException e) {
624 inprogress.remove( request );
625 failures.put(request, e);
627 } catch (Throwable t) {
628 BindingConstructionException bce = new BindingConstructionException( t );
629 inprogress.remove( request );
630 failures.put(request, bce);
635 boolean isComplete(Binding binding, IdentityHashSet<Binding> checked) {
636 for (Binding b : inprogress.values())
637 if (b == binding) return false;
639 if (checked.contains(binding)) return true;
641 if (binding.getComponentCount() > 0) {
642 checked.add(binding);
643 for (int i = 0; i < binding.getComponentCount(); i++) {
644 if (!isComplete(binding.getComponentBinding(i), checked))
653 * Get a binding to a Java Class. Type details can be modified with annotations.
654 * Please see the package org.simantics.databoard.annotations.
657 * The whether the result binding is a completely mutable or not depends on the
658 * provided classes. For instance, fields such as Boolean, Integer, Long
659 * are not mutable, instead MutableBoolean, MutableInteger and MutableLong are.
660 * The length of Object[] is not mutable, but length of List<Object> is. <p>
662 * Note, results are stored with strong reference into the repository assigned
663 * to this factory.<p>
665 * @see ClassBindingFactory
668 * @throws BindingConstructionException
670 @SuppressWarnings("unchecked")
671 public <T extends Binding> T getBinding(Class<?> clazz)
672 throws BindingConstructionException
674 return (T) construct( new BindingRequest(clazz) );
678 * Get a binding to a Java Class. Use this method to acquire class
679 * parameters for a generics class. <p>
683 * Binding binding = getBinding(Map.class, String.class, Integer.class);
684 * Map<String, Integer> map = (Map<String, Integer>) binding.createDefault();
688 * Binding d = getBinding(List.class, Integer.class);
689 * List<Integer> list = (List<Integer>) d.createRandom(5);
693 * Binding d = getBinding(List.class, List.class, Integer.class);
694 * List<List<Integer>> list = (List<List<Integer>>) d.createRandom(5);
696 * @see ClassBindingFactory
699 * @throws BindingConstructionException
701 @SuppressWarnings("unchecked")
702 public <T extends Binding> T getBinding(Class<?> clazz, Class<?>...parameters)
703 throws BindingConstructionException
705 Arguments args = new ArgumentImpl(parameters);
706 BindingRequest request = new BindingRequest( clazz, args );
707 return (T) construct( request );
710 public Binding getBinding(BindingRequest request) throws BindingConstructionException {
711 return construct(request);
714 public Binding getBindingUnchecked(BindingRequest request) throws RuntimeBindingConstructionException {
716 return construct(request);
717 } catch (BindingConstructionException e) {
718 throw new RuntimeBindingConstructionException(e);
722 static Class<?>[] NO_CLASSES = new Class<?>[0];
724 public static Annotation[] getFieldAnnotations(Field field)
726 Annotation[] annotations = field.getAnnotations().clone();
727 ArrayList<Class<?>> list = new ArrayList<Class<?>>();
728 getTypes( field.getGenericType(), list );
729 Class<?> fieldClass = list.remove(0);
730 Class<?>[] parameterClasses = list.isEmpty() ? NO_CLASSES : list.toArray( NO_CLASSES );
732 if (Set.class.isAssignableFrom(fieldClass) && parameterClasses!=null &¶meterClasses.length==1) {
733 Annotation[] a2 = new Annotation[annotations.length+1];
734 System.arraycopy(annotations, 0, a2, 0, annotations.length);
736 Class<?> keyType = parameterClasses[0];
737 a2[annotations.length] = new ArgumentImpl(keyType);
740 if (Map.class.isAssignableFrom(fieldClass) && parameterClasses!=null && parameterClasses.length==2) {
741 Annotation[] a2 = new Annotation[annotations.length+1];
742 System.arraycopy(annotations, 0, a2, 0, annotations.length);
744 Class<?> keyType = parameterClasses[0];
745 Class<?> valueType = parameterClasses[1];
746 a2[annotations.length] = new ArgumentImpl(keyType, valueType);
749 if (List.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 Class<?> componentType = parameterClasses[0];
753 a2[annotations.length] = new ArgumentImpl(componentType);
757 if (parameterClasses!=null && parameterClasses.length>0) {
758 Annotation[] a2 = new Annotation[annotations.length+1];
759 System.arraycopy(annotations, 0, a2, 0, annotations.length);
760 a2[annotations.length] = new ArgumentImpl(parameterClasses);
767 static void getTypes(Type type, Collection<Class<?>> result)
769 if ( type instanceof Class ) {
770 result.add( (Class<?>) type );
772 if ( type instanceof ParameterizedType ) {
773 ParameterizedType p = (ParameterizedType) type;
774 getTypes( p.getRawType(), result );
775 for ( Type x : p.getActualTypeArguments() ) getTypes(x, result);
777 if ( type instanceof GenericArrayType) {
778 GenericArrayType at = (GenericArrayType) type;
779 Type componentType = at.getGenericComponentType();
780 ArrayList<Class<?>> list = new ArrayList<Class<?>>(1);
781 getTypes( componentType, list );
783 Object dummy = Array.newInstance(list.get(0), 0);
784 result.add( dummy.getClass() );
785 } else if ( type instanceof TypeVariable ) {
786 result.add( Object.class );
788 throw new RuntimeException( type.getClass()+ " is not implemented" );
792 * Get all actual parameters types, incl. classes and generic types
797 static Type[] getParameterTypes(Field f) {
798 Type t = f.getGenericType();
799 if (t==null || t instanceof ParameterizedType==false) return new Class[0];
800 ParameterizedType p = (ParameterizedType) t;
801 return p.getActualTypeArguments();