1 package org.simantics.databoard.binding.reflection;
\r
3 import java.lang.annotation.Annotation;
\r
4 import java.lang.reflect.Array;
\r
5 import java.lang.reflect.Constructor;
\r
6 import java.lang.reflect.Field;
\r
7 import java.lang.reflect.GenericArrayType;
\r
8 import java.lang.reflect.InvocationTargetException;
\r
9 import java.lang.reflect.Modifier;
\r
10 import java.lang.reflect.ParameterizedType;
\r
11 import java.lang.reflect.Type;
\r
12 import java.lang.reflect.TypeVariable;
\r
13 import java.util.ArrayList;
\r
14 import java.util.Collection;
\r
15 import java.util.HashMap;
\r
16 import java.util.List;
\r
17 import java.util.Map;
\r
18 import java.util.Set;
\r
19 import java.util.concurrent.CopyOnWriteArrayList;
\r
21 import org.simantics.databoard.Bindings;
\r
22 import org.simantics.databoard.annotations.ArgumentImpl;
\r
23 import org.simantics.databoard.annotations.Arguments;
\r
24 import org.simantics.databoard.annotations.Identifier;
\r
25 import org.simantics.databoard.annotations.Length;
\r
26 import org.simantics.databoard.annotations.MIMEType;
\r
27 import org.simantics.databoard.annotations.Name;
\r
28 import org.simantics.databoard.annotations.Optional;
\r
29 import org.simantics.databoard.annotations.Pattern;
\r
30 import org.simantics.databoard.annotations.Range;
\r
31 import org.simantics.databoard.annotations.Referable;
\r
32 import org.simantics.databoard.annotations.Union;
\r
33 import org.simantics.databoard.annotations.Unit;
\r
34 import org.simantics.databoard.binding.ArrayBinding;
\r
35 import org.simantics.databoard.binding.Binding;
\r
36 import org.simantics.databoard.binding.MapBinding;
\r
37 import org.simantics.databoard.binding.OptionalBinding;
\r
38 import org.simantics.databoard.binding.RecordBinding;
\r
39 import org.simantics.databoard.binding.error.BindingConstructionException;
\r
40 import org.simantics.databoard.binding.error.RuntimeBindingConstructionException;
\r
41 import org.simantics.databoard.binding.factory.BindingRepository;
\r
42 import org.simantics.databoard.binding.factory.DefaultBindingFactory;
\r
43 import org.simantics.databoard.binding.factory.TypeBindingFactory;
\r
44 import org.simantics.databoard.binding.impl.ByteBindingDefault;
\r
45 import org.simantics.databoard.binding.impl.DoubleBindingDefault;
\r
46 import org.simantics.databoard.binding.impl.FloatBindingDefault;
\r
47 import org.simantics.databoard.binding.impl.IntegerBindingDefault;
\r
48 import org.simantics.databoard.binding.impl.LongBindingDefault;
\r
49 import org.simantics.databoard.binding.impl.OptionalBindingDefault;
\r
50 import org.simantics.databoard.binding.impl.StringBindingDefault;
\r
51 import org.simantics.databoard.binding.impl.UnsignedByteBinding;
\r
52 import org.simantics.databoard.binding.impl.UnsignedIntegerBinding;
\r
53 import org.simantics.databoard.binding.impl.UnsignedLongBinding;
\r
54 import org.simantics.databoard.binding.mutable.MutableByteBinding;
\r
55 import org.simantics.databoard.binding.mutable.MutableDoubleBinding;
\r
56 import org.simantics.databoard.binding.mutable.MutableFloatBinding;
\r
57 import org.simantics.databoard.binding.mutable.MutableIntegerBinding;
\r
58 import org.simantics.databoard.binding.mutable.MutableLongBinding;
\r
59 import org.simantics.databoard.binding.mutable.MutableStringBinding;
\r
60 import org.simantics.databoard.primitives.MutableBoolean;
\r
61 import org.simantics.databoard.primitives.MutableByte;
\r
62 import org.simantics.databoard.primitives.MutableDouble;
\r
63 import org.simantics.databoard.primitives.MutableFloat;
\r
64 import org.simantics.databoard.primitives.MutableInteger;
\r
65 import org.simantics.databoard.primitives.MutableLong;
\r
66 import org.simantics.databoard.primitives.MutableString;
\r
67 import org.simantics.databoard.primitives.UnsignedByte;
\r
68 import org.simantics.databoard.primitives.UnsignedInteger;
\r
69 import org.simantics.databoard.primitives.UnsignedLong;
\r
70 import org.simantics.databoard.type.ArrayType;
\r
71 import org.simantics.databoard.type.ByteType;
\r
72 import org.simantics.databoard.type.Component;
\r
73 import org.simantics.databoard.type.DoubleType;
\r
74 import org.simantics.databoard.type.FloatType;
\r
75 import org.simantics.databoard.type.IntegerType;
\r
76 import org.simantics.databoard.type.LongType;
\r
77 import org.simantics.databoard.type.MapType;
\r
78 import org.simantics.databoard.type.OptionalType;
\r
79 import org.simantics.databoard.type.RecordType;
\r
80 import org.simantics.databoard.type.StringType;
\r
81 import org.simantics.databoard.type.UnionType;
\r
82 import org.simantics.databoard.util.ArrayUtils;
\r
83 import org.simantics.databoard.util.IdentityHashSet;
\r
84 import org.simantics.databoard.util.RangeException;
\r
87 * Type Factory constructs data types from reflection requests.
\r
88 * Successfully constructed types are placed in the repository that was given
\r
89 * at construction time.
\r
91 * @author Toni Kalajainen
\r
93 public class ClassBindingFactory {
\r
96 * Map of failed constructions.
\r
98 Map<BindingRequest, BindingConstructionException> failures = new HashMap<BindingRequest, BindingConstructionException>();
\r
101 * Map that contains in incomplete constructions.
\r
103 Map<BindingRequest, Binding> inprogress = new HashMap<BindingRequest, Binding>();
\r
106 * Repository where successful constructions are placed.
\r
108 BindingRepository repository;
\r
110 List<BindingProvider> subFactories = new CopyOnWriteArrayList<BindingProvider>();
\r
112 /** Asm based binding factory for Classes, this is used if Asm library is available, else null */
\r
113 RecordBindingProvider asmClassBindingFactory;
\r
115 /** Reflection based binding factory for Classes, this has poorer performance than asm factory */
\r
116 RecordBindingProvider refClassBindingFactory;
\r
118 /** Factory for creating default bindings for data types. */
\r
119 TypeBindingFactory defaultBindingFactory;
\r
122 * Construct a new reflection binding factory
\r
124 public ClassBindingFactory() {
\r
126 this.repository = new BindingRepository();
\r
127 this.defaultBindingFactory = new DefaultBindingFactory();
\r
131 * Construct a new reflection binding factory that places constructed
\r
132 * bindings into user given repository.
\r
134 * @param repository
\r
136 public ClassBindingFactory(BindingRepository repository, TypeBindingFactory defaultBindingFactory) {
\r
138 this.repository = repository;
\r
139 this.defaultBindingFactory = defaultBindingFactory;
\r
143 refClassBindingFactory = new ReflectionBindingProvider();
\r
145 // Check ASM Exists
\r
146 Class.forName("org.objectweb.asm.ClassWriter");
\r
148 Class<?> y = Class.forName("org.simantics.databoard.binding.reflection.AsmBindingProvider");
\r
149 Constructor<?> c = y.getConstructor();
\r
150 asmClassBindingFactory = (RecordBindingProvider) c.newInstance();
\r
152 } catch (ClassNotFoundException e) {
\r
153 } catch (InstantiationException e) {
\r
154 } catch (IllegalAccessException e) {
\r
155 } catch (IllegalArgumentException e) {
\r
156 } catch (InvocationTargetException e) {
\r
157 } catch (SecurityException e) {
\r
158 } catch (NoSuchMethodException e) {
\r
162 public void addFactory(BindingProvider factory)
\r
164 if (!subFactories.contains(factory)) {
\r
165 this.subFactories.add(factory);
\r
169 public void removeFactory(BindingProvider factory) {
\r
170 subFactories.remove(factory);
\r
173 public BindingRepository getRepository() {
\r
178 * Constructs a binding to comply to class request.
\r
179 * This is the method sub-classes implement.
\r
180 * The implementation should use the inprogress -map for construction of
\r
181 * bindings that have component types.
\r
184 * inprogress.put(request, notCompletelyConstructedBinding);
\r
185 * Binding componentBinding = construct( componentRequest );
\r
186 * notCompletelyConstructedBinding.setChild( componentBinding );
\r
187 * inprogress.remove(request);
\r
189 * try-finally is not needed.
\r
193 * @throws BindingConstructionException
\r
194 * @throws RangeException
\r
196 @SuppressWarnings("unchecked")
\r
197 protected Binding doConstruct(BindingRequest request) throws BindingConstructionException, RangeException
\r
200 if(request.hasAnnotation(Optional.class))
\r
202 Optional optional = request.getAnnotation(Optional.class);
\r
203 Annotation[] newAnnotations = ArrayUtils.dropElements(request.annotations, optional);
\r
204 BindingRequest componentRequest = new BindingRequest(request.getClazz(), newAnnotations);
\r
205 OptionalType type = new OptionalType();
\r
206 OptionalBinding binding = new OptionalBindingDefault(type, null);
\r
207 inprogress.put(request, binding);
\r
208 binding.componentBinding = construct( componentRequest );
\r
209 type.componentType = binding.componentBinding.type();
\r
210 inprogress.remove(request);
\r
219 Range range = request.getAnnotation(Range.class);
\r
220 Unit unit = request.getAnnotation(Unit.class);
\r
222 if (request.getClazz() == Integer.class || request.getClazz() == int.class || request.getClazz() == MutableInteger.class || UnsignedInteger.class.isAssignableFrom(request.getClazz())) {
\r
223 Binding binding = null;
\r
224 if (range==null && unit==null) {
\r
225 if (request.getClazz() == int.class) binding = Bindings.INTEGER;
\r
226 if (request.getClazz() == Integer.class) binding = Bindings.INTEGER;
\r
227 if (request.getClazz() == MutableInteger.class) binding = Bindings.MUTABLE_INTEGER;
\r
228 if (request.getClazz() == UnsignedInteger.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_INTEGER;
\r
229 if (request.getClazz() == UnsignedInteger.Immutable.class) binding = Bindings.UNSIGNED_INTEGER;
\r
231 IntegerType type = new IntegerType();
\r
232 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
\r
233 type.setUnit( unit==null ? null : unit.value() );
\r
235 if (request.getClazz() == int.class) binding = new IntegerBindingDefault(type);
\r
236 if (request.getClazz() == Integer.class) binding = new IntegerBindingDefault(type);
\r
237 if (request.getClazz() == MutableInteger.class) binding = new MutableIntegerBinding(type);
\r
238 if (request.getClazz() == UnsignedInteger.Mutable.class) binding = new UnsignedIntegerBinding.Mutable(type);
\r
239 if (request.getClazz() == UnsignedInteger.Immutable.class) binding = new UnsignedIntegerBinding.Immutable(type);
\r
241 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
\r
243 repository.put(request, binding);
\r
247 if (request.getClazz() == Byte.class || request.getClazz() == byte.class || request.getClazz() == MutableByte.class || UnsignedByte.class.isAssignableFrom(request.getClazz())) {
\r
248 Binding binding = null;
\r
249 if (range==null && unit==null) {
\r
250 if (request.getClazz() == byte.class) binding = Bindings.BYTE;
\r
251 if (request.getClazz() == Byte.class) binding = Bindings.BYTE;
\r
252 if (request.getClazz() == MutableByte.class) binding = Bindings.MUTABLE_BYTE;
\r
253 if (request.getClazz() == UnsignedByte.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_BYTE;
\r
254 if (request.getClazz() == UnsignedByte.Immutable.class) binding = Bindings.UNSIGNED_BYTE;
\r
256 ByteType type = new ByteType();
\r
257 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
\r
258 type.setUnit( unit==null ? null : unit.value() );
\r
260 if (request.getClazz() == byte.class) binding = new ByteBindingDefault(type);
\r
261 if (request.getClazz() == Byte.class) binding = new ByteBindingDefault(type);
\r
262 if (request.getClazz() == MutableByte.class) binding = new MutableByteBinding(type);
\r
263 if (request.getClazz() == UnsignedByte.Mutable.class) binding = new UnsignedByteBinding.Mutable(type);
\r
264 if (request.getClazz() == UnsignedByte.Immutable.class) binding = new UnsignedByteBinding.Immutable(type);
\r
266 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
\r
268 repository.put(request, binding);
\r
272 if (request.getClazz() == Long.class || request.getClazz() == long.class || request.getClazz() == MutableLong.class || UnsignedLong.class.isAssignableFrom(request.getClazz())) {
\r
273 Binding binding = null;
\r
274 if (range==null && unit==null) {
\r
275 if (request.getClazz() == long.class) binding = Bindings.LONG;
\r
276 if (request.getClazz() == Long.class) binding = Bindings.LONG;
\r
277 if (request.getClazz() == MutableLong.class) binding = Bindings.MUTABLE_LONG;
\r
278 if (request.getClazz() == UnsignedLong.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_LONG;
\r
279 if (request.getClazz() == UnsignedLong.Immutable.class) binding = Bindings.UNSIGNED_LONG;
\r
281 LongType type = new LongType();
\r
282 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
\r
283 type.setUnit( unit==null ? null : unit.value() );
\r
285 if (request.getClazz() == long.class) binding = new LongBindingDefault(type);
\r
286 if (request.getClazz() == Long.class) binding = new LongBindingDefault(type);
\r
287 if (request.getClazz() == MutableLong.class) binding = new MutableLongBinding(type);
\r
288 if (request.getClazz() == UnsignedLong.Mutable.class) binding = new UnsignedLongBinding.Mutable(type);
\r
289 if (request.getClazz() == UnsignedLong.Immutable.class) binding = new UnsignedLongBinding.Immutable(type);
\r
291 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
\r
293 repository.put(request, binding);
\r
297 if (request.getClazz() == Float.class || request.getClazz() == float.class || request.getClazz() == MutableFloat.class) {
\r
298 Binding binding = null;
\r
299 if (range==null && unit==null) {
\r
300 if (request.getClazz() == float.class) binding = Bindings.FLOAT;
\r
301 if (request.getClazz() == Float.class) binding = Bindings.FLOAT;
\r
302 if (request.getClazz() == MutableFloat.class) binding = Bindings.MUTABLE_FLOAT;
\r
304 FloatType type = new FloatType();
\r
305 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
\r
306 type.setUnit( unit==null ? null : unit.value() );
\r
308 if (request.getClazz() == float.class) binding = new FloatBindingDefault(type);
\r
309 if (request.getClazz() == Float.class) binding = new FloatBindingDefault(type);
\r
310 if (request.getClazz() == MutableFloat.class) binding = new MutableFloatBinding(type);
\r
312 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
\r
314 repository.put(request, binding);
\r
318 if (request.getClazz() == Double.class || request.getClazz() == double.class || request.getClazz() == MutableDouble.class) {
\r
319 Binding binding = null;
\r
320 if (range==null && unit==null) {
\r
321 if (request.getClazz() == double.class) binding = Bindings.DOUBLE;
\r
322 if (request.getClazz() == Double.class) binding = Bindings.DOUBLE;
\r
323 if (request.getClazz() == MutableDouble.class) binding = Bindings.MUTABLE_DOUBLE;
\r
325 DoubleType type = new DoubleType();
\r
326 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
\r
327 type.setUnit( unit==null ? null : unit.value() );
\r
329 if (request.getClazz() == double.class) binding = new DoubleBindingDefault(type);
\r
330 if (request.getClazz() == Double.class) binding = new DoubleBindingDefault(type);
\r
331 if (request.getClazz() == MutableDouble.class) binding = new MutableDoubleBinding(type);
\r
334 repository.put(request, binding);
\r
338 if (request.getClazz() == Boolean.class || request.getClazz() == boolean.class || request.getClazz() == MutableBoolean.class) {
\r
339 Binding binding = null;
\r
340 if (request.getClazz() == Boolean.class || request.getClazz() == boolean.class) binding = Bindings.BOOLEAN;
\r
341 if (request.getClazz() == MutableBoolean.class) binding = Bindings.MUTABLE_BOOLEAN;
\r
342 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
\r
344 repository.put(request, binding);
\r
348 if (request.getClazz() == String.class || request.getClazz() == MutableString.class) {
\r
349 Length length = request.getAnnotation(Length.class);
\r
350 MIMEType mimeType = request.getAnnotation(MIMEType.class);
\r
351 Pattern pattern = request.getAnnotation(Pattern.class);
\r
352 Binding binding = null;
\r
353 if (length==null && mimeType==null && pattern==null) {
\r
354 if (request.getClazz() == String.class) binding = Bindings.STRING;
\r
355 if (request.getClazz() == MutableString.class) binding = Bindings.MUTABLE_STRING;
\r
357 StringType type = new StringType();
\r
358 type.setLength( length==null ? null : org.simantics.databoard.util.Range.valueOf( length.value()[0] ) );
\r
359 type.setMimeType( mimeType==null ? null : mimeType.value() );
\r
360 type.setPattern( pattern==null ? null : pattern.value() );
\r
361 if (request.getClazz() == String.class) binding = new StringBindingDefault(type);
\r
362 if (request.getClazz() == MutableString.class) binding = new MutableStringBinding(type);
\r
364 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
\r
366 repository.put(request, binding);
\r
372 // Custom factories
\r
373 for (BindingProvider factory : subFactories) {
\r
374 Binding result = factory.provideBinding(this, request);
\r
375 if (result == null) continue;
\r
378 // If the result was an arraybinding, complete the composite binding
\r
379 if (result instanceof ArrayBinding) {
\r
380 ArrayBinding binding = (ArrayBinding) result;
\r
381 ArrayType type = binding.type();
\r
382 Length lengthAnnotation = request.getAnnotation(Length.class);
\r
383 Arguments argumentsAnnotation = request.getAnnotation(Arguments.class);
\r
384 Annotation[] componentAnnotations = request.dropAnnotations(1, lengthAnnotation);
\r
385 Class<?> componentClass = argumentsAnnotation!=null ? argumentsAnnotation.value()[0] : request.getClazz().getComponentType();
\r
387 org.simantics.databoard.util.Range[] lengths = null;
\r
388 if (lengthAnnotation!=null) {
\r
389 String[] strs = lengthAnnotation.value();
\r
390 lengths = new org.simantics.databoard.util.Range[strs.length];
\r
391 for (int i=0; i<strs.length; i++)
\r
392 lengths[i] = org.simantics.databoard.util.Range.valueOf(strs[i]);
\r
395 if ( binding.componentBinding==null && request.componentBindings!=null ) binding.componentBinding = request.componentBindings[0];
\r
396 if ( binding.componentBinding == null) {
\r
397 BindingRequest componentRequest = request.componentRequests != null ? request.componentRequests[0] : null;
\r
398 if (componentRequest==null) {
\r
399 if (componentClass==null) {
\r
400 componentClass = Object.class;
\r
401 // throw new BindingConstructionException("Cannot determine array component type");
\r
403 componentRequest = new BindingRequest(componentClass, componentAnnotations);
\r
406 inprogress.put(request, binding);
\r
407 binding.componentBinding = construct( componentRequest );
\r
408 inprogress.remove(request);
\r
411 type.componentType = binding.componentBinding.type();
\r
413 // Multi-dimensional Array. Apply range to sub-array-types
\r
414 ArrayType t = type;
\r
415 if (lengths!=null) {
\r
416 for (int i=0; i<lengths.length; i++) {
\r
417 t.setLength( lengths[i] );
\r
418 if (i<lengths.length-1)
\r
419 t = (ArrayType) t.componentType;
\r
426 if ( result instanceof MapBinding ) {
\r
427 MapBinding binding = (MapBinding) result;
\r
429 Arguments argumentsAnnotation = request.getAnnotation(Arguments.class);
\r
430 Annotation[] componentAnnotations = request.dropAnnotations( 2 );
\r
431 Class<?>[] arguments = argumentsAnnotation != null ? argumentsAnnotation.value() : null;
\r
433 BindingRequest keyRequest = null;
\r
434 BindingRequest valueRequest = null;
\r
436 Binding keyBinding = null;
\r
437 Binding valueBinding = null;
\r
439 if (binding.getKeyBinding() != null) {
\r
440 keyBinding = binding.getKeyBinding();
\r
442 else if (request.componentBindings != null) {
\r
443 keyBinding = request.componentBindings[0];
\r
445 else if (request.componentRequests != null) {
\r
446 keyRequest = request.componentRequests[0];
\r
449 Class<?> keyClass = arguments != null && arguments.length >= 1 ? arguments[0] : null;
\r
450 if (keyClass==null) {
\r
451 keyClass = Object.class;
\r
452 //throw new BindingConstructionException("Cannot determine key class, use @Arguments annotation");
\r
454 keyRequest = new BindingRequest(keyClass, componentAnnotations);
\r
457 if (binding.getValueBinding() != null) {
\r
458 valueBinding = binding.getValueBinding();
\r
460 else if (request.componentBindings != null) {
\r
461 valueBinding = request.componentBindings[1];
\r
463 else if (request.componentRequests != null) {
\r
464 valueRequest = request.componentRequests[1];
\r
467 Class<?> valueClass = arguments != null && arguments.length >= 2 ? arguments[1] : null;
\r
468 if (valueClass==null) {
\r
469 valueClass = Object.class;
\r
470 //throw new BindingConstructionException("Cannot determine value class, use @Arguments annotation");
\r
472 valueRequest = new BindingRequest(valueClass, componentAnnotations);
\r
475 inprogress.put(request, result);
\r
476 if (keyRequest!=null) {
\r
477 keyBinding = construct( keyRequest );
\r
479 if (valueRequest!=null) {
\r
480 valueBinding = construct( valueRequest );
\r
482 inprogress.remove(request);
\r
484 MapType type = binding.type();
\r
485 type.keyType = keyBinding.type();
\r
486 type.valueType = valueBinding.type();
\r
487 binding.setKeyBinding( keyBinding );
\r
488 binding.setValueBinding( valueBinding );
\r
499 // Its complete, store to repository
\r
500 repository.put(request, result);
\r
506 if (request.getClazz().isEnum()) {
\r
507 Enum<?>[] enums = (Enum[]) request.getClazz().getEnumConstants();
\r
508 UnionType type = new UnionType();
\r
509 type.components = new Component[enums.length];
\r
510 for (int i=0; i<enums.length; i++) {
\r
511 String name = enums[i].name();
\r
512 type.components[i] = new Component(name, new RecordType(false));
\r
514 EnumClassBinding binding = new EnumClassBinding(type, (Class<Enum<?>>) request.getClazz() );
\r
515 repository.put(request, binding);
\r
520 if(request.hasAnnotation(Union.class)) {
\r
521 Union union = request.getAnnotation(Union.class);
\r
522 UnionType type = new UnionType();
\r
523 UnionClassBinding binding = new UnionClassBinding(type);
\r
524 Class<?>[] cases = union.value();
\r
525 int count = cases.length;
\r
526 Binding[] componentBindings = new Binding[count];
\r
527 type.components = new Component[ count ];
\r
528 binding.componentClasses = new Class<?>[ count ];
\r
529 binding.setComponentBindings(componentBindings);
\r
531 inprogress.put(request, binding);
\r
532 for(int i=0;i<count;++i) {
\r
533 binding.componentClasses[i]= cases[i];
\r
534 Component component = type.components[i] = new Component(cases[i].getSimpleName(), null /* updated later*/);
\r
535 BindingRequest componentRequest = new BindingRequest( cases[i] );
\r
536 Binding componentBinding = componentBindings[i] = construct( componentRequest );
\r
537 component.type = componentBinding.type();
\r
540 inprogress.remove(request);
\r
541 repository.put(request, binding);
\r
547 RecordType type = new RecordType();
\r
548 ClassInfo ci = ClassInfo.getInfo( request.getClazz() );
\r
549 boolean publicClass = Modifier.isPublic( request.getClazz().getModifiers() );
\r
550 boolean useAsmBinding = asmClassBindingFactory!=null && publicClass;
\r
551 RecordBindingProvider f = useAsmBinding ? asmClassBindingFactory : refClassBindingFactory;
\r
552 RecordBinding binding = f.provideRecordBinding( request.getClazz(), type);
\r
553 int count = ci.fields.length;
\r
554 Binding[] componentBindings = binding.getComponentBindings();
\r
555 Component[] components = new Component[ count ];
\r
556 type.setReferable( request.getAnnotation(Referable.class) != null );
\r
557 type.setComponents( components );
\r
559 inprogress.put(request, binding);
\r
560 List<Integer> identifierIndices = new ArrayList<Integer>(1);
\r
561 for(int i=0;i<type.getComponentCount();++i) {
\r
562 Field field = ci.fields[i];
\r
563 Annotation[] annotations = getFieldAnnotations(field);
\r
564 Class<?> fieldClass = field.getType();
\r
565 BindingRequest componentRequest = new BindingRequest(fieldClass, annotations);
\r
567 Name nameAnnotation = componentRequest.getAnnotation( Name.class );
\r
568 String fieldName = nameAnnotation!=null ? nameAnnotation.value() : field.getName();
\r
569 Component c = components[i] = new Component(fieldName, null /* updated later */);
\r
571 Identifier idAnnotation = componentRequest.getAnnotation( Identifier.class );
\r
572 if ( idAnnotation!=null ) {
\r
573 componentRequest.dropAnnotations(1, idAnnotation);
\r
574 identifierIndices.add( i );
\r
576 Binding componentBinding = componentBindings[i] = construct( componentRequest );
\r
577 c.type = componentBinding.type();
\r
579 type.setIdentifiers(identifierIndices);
\r
580 inprogress.remove(request);
\r
581 repository.put(request, binding);
\r
586 public Binding construct(BindingRequest request)
\r
587 throws BindingConstructionException
\r
589 if (failures.containsKey(request)) throw failures.get(request);
\r
590 if (inprogress.containsKey(request)) return inprogress.get(request);
\r
591 if (repository.containsRequest(request)) return repository.get(request);
\r
593 // Start construction
\r
595 Binding binding = doConstruct(request);
\r
597 // Avoid creating duplicate binding instances
\r
598 // Only check bindings that are complete
\r
599 if (inprogress.isEmpty() || isComplete(binding, new IdentityHashSet<Binding>())) {
\r
600 Binding defaultBinding = defaultBindingFactory.getBinding(binding.type());
\r
601 if (defaultBinding != null && defaultBinding.equals(binding))
\r
602 binding = defaultBinding;
\r
605 repository.put(request, binding);
\r
608 } catch (RangeException e) {
\r
609 inprogress.remove( request );
\r
610 BindingConstructionException bce = new BindingConstructionException( e );
\r
611 failures.put(request, bce);
\r
613 } catch (BindingConstructionException e) {
\r
614 inprogress.remove( request );
\r
615 failures.put(request, e);
\r
617 } catch (Throwable t) {
\r
618 BindingConstructionException bce = new BindingConstructionException( t );
\r
619 inprogress.remove( request );
\r
620 failures.put(request, bce);
\r
625 boolean isComplete(Binding binding, IdentityHashSet<Binding> checked) {
\r
626 for (Binding b : inprogress.values())
\r
627 if (b == binding) return false;
\r
629 if (checked.contains(binding)) return true;
\r
631 if (binding.getComponentCount() > 0) {
\r
632 checked.add(binding);
\r
633 for (int i = 0; i < binding.getComponentCount(); i++) {
\r
634 if (!isComplete(binding.getComponentBinding(i), checked))
\r
643 * Get a binding to a Java Class. Type details can be modified with annotations.
\r
644 * Please see the package org.simantics.databoard.annotations.
\r
647 * The whether the result binding is a completely mutable or not depends on the
\r
648 * provided classes. For instance, fields such as Boolean, Integer, Long
\r
649 * are not mutable, instead MutableBoolean, MutableInteger and MutableLong are.
\r
650 * The length of Object[] is not mutable, but length of List<Object> is. <p>
\r
652 * Note, results are stored with strong reference into the repository assigned
\r
653 * to this factory.<p>
\r
655 * @see ClassBindingFactory
\r
658 * @throws BindingConstructionException
\r
660 @SuppressWarnings("unchecked")
\r
661 public <T extends Binding> T getBinding(Class<?> clazz)
\r
662 throws BindingConstructionException
\r
664 return (T) construct( new BindingRequest(clazz) );
\r
668 * Get a binding to a Java Class. Use this method to acquire class
\r
669 * parameters for a generics class. <p>
\r
673 * Binding binding = getBinding(Map.class, String.class, Integer.class);
\r
674 * Map<String, Integer> map = (Map<String, Integer>) binding.createDefault();
\r
678 * Binding d = getBinding(List.class, Integer.class);
\r
679 * List<Integer> list = (List<Integer>) d.createRandom(5);
\r
683 * Binding d = getBinding(List.class, List.class, Integer.class);
\r
684 * List<List<Integer>> list = (List<List<Integer>>) d.createRandom(5);
\r
686 * @see ClassBindingFactory
\r
689 * @throws BindingConstructionException
\r
691 @SuppressWarnings("unchecked")
\r
692 public <T extends Binding> T getBinding(Class<?> clazz, Class<?>...parameters)
\r
693 throws BindingConstructionException
\r
695 Arguments args = new ArgumentImpl(parameters);
\r
696 BindingRequest request = new BindingRequest( clazz, args );
\r
697 return (T) construct( request );
\r
700 public Binding getBinding(BindingRequest request) throws BindingConstructionException {
\r
701 return construct(request);
\r
704 public Binding getBindingUnchecked(BindingRequest request) throws RuntimeBindingConstructionException {
\r
706 return construct(request);
\r
707 } catch (BindingConstructionException e) {
\r
708 throw new RuntimeBindingConstructionException(e);
\r
712 static Class<?>[] NO_CLASSES = new Class<?>[0];
\r
714 public static Annotation[] getFieldAnnotations(Field field)
\r
716 Annotation[] annotations = field.getAnnotations().clone();
\r
717 ArrayList<Class<?>> list = new ArrayList<Class<?>>();
\r
718 getTypes( field.getGenericType(), list );
\r
719 Class<?> fieldClass = list.remove(0);
\r
720 Class<?>[] parameterClasses = list.isEmpty() ? NO_CLASSES : list.toArray( NO_CLASSES );
\r
722 if (Set.class.isAssignableFrom(fieldClass) && parameterClasses!=null &¶meterClasses.length==1) {
\r
723 Annotation[] a2 = new Annotation[annotations.length+1];
\r
724 System.arraycopy(annotations, 0, a2, 0, annotations.length);
\r
726 Class<?> keyType = parameterClasses[0];
\r
727 a2[annotations.length] = new ArgumentImpl(keyType);
\r
730 if (Map.class.isAssignableFrom(fieldClass) && parameterClasses!=null && parameterClasses.length==2) {
\r
731 Annotation[] a2 = new Annotation[annotations.length+1];
\r
732 System.arraycopy(annotations, 0, a2, 0, annotations.length);
\r
734 Class<?> keyType = parameterClasses[0];
\r
735 Class<?> valueType = parameterClasses[1];
\r
736 a2[annotations.length] = new ArgumentImpl(keyType, valueType);
\r
739 if (List.class.isAssignableFrom(fieldClass) && parameterClasses!=null && parameterClasses.length==1) {
\r
740 Annotation[] a2 = new Annotation[annotations.length+1];
\r
741 System.arraycopy(annotations, 0, a2, 0, annotations.length);
\r
742 Class<?> componentType = parameterClasses[0];
\r
743 a2[annotations.length] = new ArgumentImpl(componentType);
\r
747 if (parameterClasses!=null && parameterClasses.length>0) {
\r
748 Annotation[] a2 = new Annotation[annotations.length+1];
\r
749 System.arraycopy(annotations, 0, a2, 0, annotations.length);
\r
750 a2[annotations.length] = new ArgumentImpl(parameterClasses);
\r
754 return annotations;
\r
757 static void getTypes(Type type, Collection<Class<?>> result)
\r
759 if ( type instanceof Class ) {
\r
760 result.add( (Class<?>) type );
\r
762 if ( type instanceof ParameterizedType ) {
\r
763 ParameterizedType p = (ParameterizedType) type;
\r
764 getTypes( p.getRawType(), result );
\r
765 for ( Type x : p.getActualTypeArguments() ) getTypes(x, result);
\r
767 if ( type instanceof GenericArrayType) {
\r
768 GenericArrayType at = (GenericArrayType) type;
\r
769 Type componentType = at.getGenericComponentType();
\r
770 ArrayList<Class<?>> list = new ArrayList<Class<?>>(1);
\r
771 getTypes( componentType, list );
\r
773 Object dummy = Array.newInstance(list.get(0), 0);
\r
774 result.add( dummy.getClass() );
\r
775 } else if ( type instanceof TypeVariable ) {
\r
776 result.add( Object.class );
\r
778 throw new RuntimeException( type.getClass()+ " is not implemented" );
\r
782 * Get all actual parameters types, incl. classes and generic types
\r
787 static Type[] getParameterTypes(Field f) {
\r
788 Type t = f.getGenericType();
\r
789 if (t==null || t instanceof ParameterizedType==false) return new Class[0];
\r
790 ParameterizedType p = (ParameterizedType) t;
\r
791 return p.getActualTypeArguments();
\r