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.OptionalType;
\r
78 import org.simantics.databoard.type.RecordType;
\r
79 import org.simantics.databoard.type.StringType;
\r
80 import org.simantics.databoard.type.UnionType;
\r
81 import org.simantics.databoard.util.ArrayUtils;
\r
82 import org.simantics.databoard.util.IdentityHashSet;
\r
83 import org.simantics.databoard.util.RangeException;
\r
86 * Type Factory constructs data types from reflection requests.
\r
87 * Successfully constructed types are placed in the repository that was given
\r
88 * at construction time.
\r
90 * @author Toni Kalajainen
\r
92 public class ClassBindingFactory {
\r
95 * Map of failed constructions.
\r
97 Map<BindingRequest, BindingConstructionException> failures = new HashMap<BindingRequest, BindingConstructionException>();
\r
100 * Map that contains in incomplete constructions.
\r
102 Map<BindingRequest, Binding> inprogress = new HashMap<BindingRequest, Binding>();
\r
105 * Repository where successful constructions are placed.
\r
107 BindingRepository repository;
\r
109 List<BindingProvider> subFactories = new CopyOnWriteArrayList<BindingProvider>();
\r
111 /** Asm based binding factory for Classes, this is used if Asm library is available, else null */
\r
112 RecordBindingProvider asmClassBindingFactory;
\r
114 /** Reflection based binding factory for Classes, this has poorer performance than asm factory */
\r
115 RecordBindingProvider refClassBindingFactory;
\r
117 /** Factory for creating default bindings for data types. */
\r
118 TypeBindingFactory defaultBindingFactory;
\r
121 * Construct a new reflection binding factory
\r
123 public ClassBindingFactory() {
\r
125 this.repository = new BindingRepository();
\r
126 this.defaultBindingFactory = new DefaultBindingFactory();
\r
130 * Construct a new reflection binding factory that places constructed
\r
131 * bindings into user given repository.
\r
133 * @param repository
\r
135 public ClassBindingFactory(BindingRepository repository, TypeBindingFactory defaultBindingFactory) {
\r
137 this.repository = repository;
\r
138 this.defaultBindingFactory = defaultBindingFactory;
\r
142 refClassBindingFactory = new ReflectionBindingProvider();
\r
144 // Check ASM Exists
\r
145 Class.forName("org.objectweb.asm.ClassWriter");
\r
147 Class<?> y = Class.forName("org.simantics.databoard.binding.reflection.AsmBindingProvider");
\r
148 Constructor<?> c = y.getConstructor();
\r
149 asmClassBindingFactory = (RecordBindingProvider) c.newInstance();
\r
151 } catch (ClassNotFoundException e) {
\r
152 } catch (InstantiationException e) {
\r
153 } catch (IllegalAccessException e) {
\r
154 } catch (IllegalArgumentException e) {
\r
155 } catch (InvocationTargetException e) {
\r
156 } catch (SecurityException e) {
\r
157 } catch (NoSuchMethodException e) {
\r
161 public void addFactory(BindingProvider factory)
\r
163 if (!subFactories.contains(factory)) {
\r
164 this.subFactories.add(factory);
\r
168 public void removeFactory(BindingProvider factory) {
\r
169 subFactories.remove(factory);
\r
172 public BindingRepository getRepository() {
\r
177 * Constructs a binding to comply to class request.
\r
178 * This is the method sub-classes implement.
\r
179 * The implementation should use the inprogress -map for construction of
\r
180 * bindings that have component types.
\r
183 * inprogress.put(request, notCompletelyConstructedBinding);
\r
184 * Binding componentBinding = construct( componentRequest );
\r
185 * notCompletelyConstructedBinding.setChild( componentBinding );
\r
186 * inprogress.remove(request);
\r
188 * try-finally is not needed.
\r
192 * @throws BindingConstructionException
\r
193 * @throws RangeException
\r
195 @SuppressWarnings("unchecked")
\r
196 protected Binding doConstruct(BindingRequest request) throws BindingConstructionException, RangeException
\r
199 if(request.hasAnnotation(Optional.class))
\r
201 Optional optional = request.getAnnotation(Optional.class);
\r
202 Annotation[] newAnnotations = ArrayUtils.dropElements(request.annotations, optional);
\r
203 BindingRequest componentRequest = new BindingRequest(request.getClazz(), newAnnotations);
\r
204 OptionalType type = new OptionalType();
\r
205 OptionalBinding binding = new OptionalBindingDefault(type, null);
\r
206 inprogress.put(request, binding);
\r
207 binding.componentBinding = construct( componentRequest );
\r
208 type.componentType = binding.componentBinding.type();
\r
209 inprogress.remove(request);
\r
218 Range range = request.getAnnotation(Range.class);
\r
219 Unit unit = request.getAnnotation(Unit.class);
\r
221 if (request.getClazz() == Integer.class || request.getClazz() == int.class || request.getClazz() == MutableInteger.class || UnsignedInteger.class.isAssignableFrom(request.getClazz())) {
\r
222 Binding binding = null;
\r
223 if (range==null && unit==null) {
\r
224 if (request.getClazz() == int.class) binding = Bindings.INTEGER;
\r
225 if (request.getClazz() == Integer.class) binding = Bindings.INTEGER;
\r
226 if (request.getClazz() == MutableInteger.class) binding = Bindings.MUTABLE_INTEGER;
\r
227 if (request.getClazz() == UnsignedInteger.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_INTEGER;
\r
228 if (request.getClazz() == UnsignedInteger.Immutable.class) binding = Bindings.UNSIGNED_INTEGER;
\r
230 IntegerType type = new IntegerType();
\r
231 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
\r
232 type.setUnit( unit==null ? null : unit.value() );
\r
234 if (request.getClazz() == int.class) binding = new IntegerBindingDefault(type);
\r
235 if (request.getClazz() == Integer.class) binding = new IntegerBindingDefault(type);
\r
236 if (request.getClazz() == MutableInteger.class) binding = new MutableIntegerBinding(type);
\r
237 if (request.getClazz() == UnsignedInteger.Mutable.class) binding = new UnsignedIntegerBinding.Mutable(type);
\r
238 if (request.getClazz() == UnsignedInteger.Immutable.class) binding = new UnsignedIntegerBinding.Immutable(type);
\r
240 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
\r
242 repository.put(request, binding);
\r
246 if (request.getClazz() == Byte.class || request.getClazz() == byte.class || request.getClazz() == MutableByte.class || UnsignedByte.class.isAssignableFrom(request.getClazz())) {
\r
247 Binding binding = null;
\r
248 if (range==null && unit==null) {
\r
249 if (request.getClazz() == byte.class) binding = Bindings.BYTE;
\r
250 if (request.getClazz() == Byte.class) binding = Bindings.BYTE;
\r
251 if (request.getClazz() == MutableByte.class) binding = Bindings.MUTABLE_BYTE;
\r
252 if (request.getClazz() == UnsignedByte.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_BYTE;
\r
253 if (request.getClazz() == UnsignedByte.Immutable.class) binding = Bindings.UNSIGNED_BYTE;
\r
255 ByteType type = new ByteType();
\r
256 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
\r
257 type.setUnit( unit==null ? null : unit.value() );
\r
259 if (request.getClazz() == byte.class) binding = new ByteBindingDefault(type);
\r
260 if (request.getClazz() == Byte.class) binding = new ByteBindingDefault(type);
\r
261 if (request.getClazz() == MutableByte.class) binding = new MutableByteBinding(type);
\r
262 if (request.getClazz() == UnsignedByte.Mutable.class) binding = new UnsignedByteBinding.Mutable(type);
\r
263 if (request.getClazz() == UnsignedByte.Immutable.class) binding = new UnsignedByteBinding.Immutable(type);
\r
265 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
\r
267 repository.put(request, binding);
\r
271 if (request.getClazz() == Long.class || request.getClazz() == long.class || request.getClazz() == MutableLong.class || UnsignedLong.class.isAssignableFrom(request.getClazz())) {
\r
272 Binding binding = null;
\r
273 if (range==null && unit==null) {
\r
274 if (request.getClazz() == long.class) binding = Bindings.LONG;
\r
275 if (request.getClazz() == Long.class) binding = Bindings.LONG;
\r
276 if (request.getClazz() == MutableLong.class) binding = Bindings.MUTABLE_LONG;
\r
277 if (request.getClazz() == UnsignedLong.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_LONG;
\r
278 if (request.getClazz() == UnsignedLong.Immutable.class) binding = Bindings.UNSIGNED_LONG;
\r
280 LongType type = new LongType();
\r
281 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
\r
282 type.setUnit( unit==null ? null : unit.value() );
\r
284 if (request.getClazz() == long.class) binding = new LongBindingDefault(type);
\r
285 if (request.getClazz() == Long.class) binding = new LongBindingDefault(type);
\r
286 if (request.getClazz() == MutableLong.class) binding = new MutableLongBinding(type);
\r
287 if (request.getClazz() == UnsignedLong.Mutable.class) binding = new UnsignedLongBinding.Mutable(type);
\r
288 if (request.getClazz() == UnsignedLong.Immutable.class) binding = new UnsignedLongBinding.Immutable(type);
\r
290 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
\r
292 repository.put(request, binding);
\r
296 if (request.getClazz() == Float.class || request.getClazz() == float.class || request.getClazz() == MutableFloat.class) {
\r
297 Binding binding = null;
\r
298 if (range==null && unit==null) {
\r
299 if (request.getClazz() == float.class) binding = Bindings.FLOAT;
\r
300 if (request.getClazz() == Float.class) binding = Bindings.FLOAT;
\r
301 if (request.getClazz() == MutableFloat.class) binding = Bindings.MUTABLE_FLOAT;
\r
303 FloatType type = new FloatType();
\r
304 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
\r
305 type.setUnit( unit==null ? null : unit.value() );
\r
307 if (request.getClazz() == float.class) binding = new FloatBindingDefault(type);
\r
308 if (request.getClazz() == Float.class) binding = new FloatBindingDefault(type);
\r
309 if (request.getClazz() == MutableFloat.class) binding = new MutableFloatBinding(type);
\r
311 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
\r
313 repository.put(request, binding);
\r
317 if (request.getClazz() == Double.class || request.getClazz() == double.class || request.getClazz() == MutableDouble.class) {
\r
318 Binding binding = null;
\r
319 if (range==null && unit==null) {
\r
320 if (request.getClazz() == double.class) binding = Bindings.DOUBLE;
\r
321 if (request.getClazz() == Double.class) binding = Bindings.DOUBLE;
\r
322 if (request.getClazz() == MutableDouble.class) binding = Bindings.MUTABLE_DOUBLE;
\r
324 DoubleType type = new DoubleType();
\r
325 type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );
\r
326 type.setUnit( unit==null ? null : unit.value() );
\r
328 if (request.getClazz() == double.class) binding = new DoubleBindingDefault(type);
\r
329 if (request.getClazz() == Double.class) binding = new DoubleBindingDefault(type);
\r
330 if (request.getClazz() == MutableDouble.class) binding = new MutableDoubleBinding(type);
\r
333 repository.put(request, binding);
\r
337 if (request.getClazz() == Boolean.class || request.getClazz() == boolean.class || request.getClazz() == MutableBoolean.class) {
\r
338 Binding binding = null;
\r
339 if (request.getClazz() == Boolean.class || request.getClazz() == boolean.class) binding = Bindings.BOOLEAN;
\r
340 if (request.getClazz() == MutableBoolean.class) binding = Bindings.MUTABLE_BOOLEAN;
\r
341 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
\r
343 repository.put(request, binding);
\r
347 if (request.getClazz() == String.class || request.getClazz() == MutableString.class) {
\r
348 Length length = request.getAnnotation(Length.class);
\r
349 MIMEType mimeType = request.getAnnotation(MIMEType.class);
\r
350 Pattern pattern = request.getAnnotation(Pattern.class);
\r
351 Binding binding = null;
\r
352 if (length==null && mimeType==null && pattern==null) {
\r
353 if (request.getClazz() == String.class) binding = Bindings.STRING;
\r
354 if (request.getClazz() == MutableString.class) binding = Bindings.MUTABLE_STRING;
\r
356 StringType type = new StringType();
\r
357 type.setLength( length==null ? null : org.simantics.databoard.util.Range.valueOf( length.value()[0] ) );
\r
358 type.setMimeType( mimeType==null ? null : mimeType.value() );
\r
359 type.setPattern( pattern==null ? null : pattern.value() );
\r
360 if (request.getClazz() == String.class) binding = new StringBindingDefault(type);
\r
361 if (request.getClazz() == MutableString.class) binding = new MutableStringBinding(type);
\r
363 if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());
\r
365 repository.put(request, binding);
\r
371 // Custom factories
\r
372 for (BindingProvider factory : subFactories) {
\r
373 Binding result = factory.provideBinding(this, request);
\r
374 if (result == null) continue;
\r
377 // If the result was an arraybinding, complete the composite binding
\r
378 if (result instanceof ArrayBinding) {
\r
379 ArrayBinding binding = (ArrayBinding) result;
\r
380 ArrayType type = binding.type();
\r
381 Length lengthAnnotation = request.getAnnotation(Length.class);
\r
382 Arguments argumentsAnnotation = request.getAnnotation(Arguments.class);
\r
383 Annotation[] componentAnnotations = request.dropAnnotations(1, lengthAnnotation);
\r
384 Class<?> componentClass = argumentsAnnotation!=null ? argumentsAnnotation.value()[0] : request.getClazz().getComponentType();
\r
386 org.simantics.databoard.util.Range[] lengths = null;
\r
387 if (lengthAnnotation!=null) {
\r
388 String[] strs = lengthAnnotation.value();
\r
389 lengths = new org.simantics.databoard.util.Range[strs.length];
\r
390 for (int i=0; i<strs.length; i++)
\r
391 lengths[i] = org.simantics.databoard.util.Range.valueOf(strs[i]);
\r
394 if ( binding.componentBinding==null && request.componentBinding!=null ) binding.componentBinding = request.componentBinding;
\r
395 if ( binding.componentBinding == null) {
\r
396 BindingRequest componentRequest = request.componentRequest;
\r
397 if (componentClass!=null && componentRequest==null) {
\r
398 componentRequest = new BindingRequest(componentClass, componentAnnotations);
\r
400 if (componentRequest==null) {
\r
401 throw new BindingConstructionException("Cannot determine array component type");
\r
404 inprogress.put(request, binding);
\r
405 binding.componentBinding = construct( componentRequest );
\r
406 inprogress.remove(request);
\r
409 type.componentType = binding.componentBinding.type();
\r
411 // Multi-dimensional Array. Apply range to sub-array-types
\r
412 ArrayType t = type;
\r
413 if (lengths!=null) {
\r
414 for (int i=0; i<lengths.length; i++) {
\r
415 t.setLength( lengths[i] );
\r
416 if (i<lengths.length-1)
\r
417 t = (ArrayType) t.componentType;
\r
424 if ( result instanceof MapBinding ) {
\r
425 MapBinding binding = (MapBinding) result;
\r
427 Arguments argumentsAnnotation = request.getAnnotation(Arguments.class);
\r
428 Annotation[] componentAnnotations = request.dropAnnotations( 2 );
\r
430 BindingRequest keyRequest = null;
\r
431 BindingRequest valueRequest = null;
\r
433 if (binding.getKeyBinding() == null) {
\r
434 Class<?> keyClass = argumentsAnnotation.value()[0];
\r
435 if (keyClass==null) {
\r
436 throw new BindingConstructionException("Cannot determine key class, use @Arguments annotation");
\r
438 keyRequest = new BindingRequest(keyClass, componentAnnotations);
\r
441 binding.type().keyType = binding.getKeyBinding().type();
\r
444 if (binding.getValueBinding() == null) {
\r
445 Class<?> valueClass = argumentsAnnotation.value()[1];
\r
446 if (valueClass==null) {
\r
447 throw new BindingConstructionException("Cannot determine value class, use @Arguments annotation");
\r
449 valueRequest = new BindingRequest(valueClass, componentAnnotations);
\r
452 binding.type().valueType = binding.getValueBinding().type();
\r
455 inprogress.put(request, result);
\r
456 if (keyRequest!=null) {
\r
457 Binding keyBinding = construct( keyRequest );
\r
458 binding.type().keyType = keyBinding.type();
\r
459 binding.setKeyBinding(keyBinding);
\r
461 if (valueRequest!=null) {
\r
462 Binding valueBinding = construct( valueRequest );
\r
463 binding.type().valueType = valueBinding.type();
\r
464 binding.setValueBinding(valueBinding);
\r
466 inprogress.remove(request);
\r
477 // Its complete, store to repository
\r
478 repository.put(request, result);
\r
484 if (request.getClazz().isEnum()) {
\r
485 Enum<?>[] enums = (Enum[]) request.getClazz().getEnumConstants();
\r
486 UnionType type = new UnionType();
\r
487 type.components = new Component[enums.length];
\r
488 for (int i=0; i<enums.length; i++) {
\r
489 String name = enums[i].name();
\r
490 type.components[i] = new Component(name, new RecordType(false));
\r
492 EnumClassBinding binding = new EnumClassBinding(type, (Class<Enum<?>>) request.getClazz() );
\r
493 repository.put(request, binding);
\r
498 if(request.hasAnnotation(Union.class)) {
\r
499 Union union = request.getAnnotation(Union.class);
\r
500 UnionType type = new UnionType();
\r
501 UnionClassBinding binding = new UnionClassBinding(type);
\r
502 Class<?>[] cases = union.value();
\r
503 int count = cases.length;
\r
504 Binding[] componentBindings = new Binding[count];
\r
505 type.components = new Component[ count ];
\r
506 binding.componentClasses = new Class<?>[ count ];
\r
507 binding.setComponentBindings(componentBindings);
\r
509 inprogress.put(request, binding);
\r
510 for(int i=0;i<count;++i) {
\r
511 binding.componentClasses[i]= cases[i];
\r
512 Component component = type.components[i] = new Component(cases[i].getSimpleName(), null /* updated later*/);
\r
513 BindingRequest componentRequest = new BindingRequest( cases[i] );
\r
514 Binding componentBinding = componentBindings[i] = construct( componentRequest );
\r
515 component.type = componentBinding.type();
\r
518 inprogress.remove(request);
\r
519 repository.put(request, binding);
\r
525 RecordType type = new RecordType();
\r
526 ClassInfo ci = ClassInfo.getInfo( request.getClazz() );
\r
527 boolean publicClass = Modifier.isPublic( request.getClazz().getModifiers() );
\r
528 boolean useAsmBinding = asmClassBindingFactory!=null && publicClass;
\r
529 RecordBindingProvider f = useAsmBinding ? asmClassBindingFactory : refClassBindingFactory;
\r
530 RecordBinding binding = f.provideRecordBinding( request.getClazz(), type);
\r
531 int count = ci.fields.length;
\r
532 Binding[] componentBindings = binding.getComponentBindings();
\r
533 Component[] components = new Component[ count ];
\r
534 type.setReferable( request.getAnnotation(Referable.class) != null );
\r
535 type.setComponents( components );
\r
537 inprogress.put(request, binding);
\r
538 List<Integer> identifierIndices = new ArrayList<Integer>(1);
\r
539 for(int i=0;i<type.getComponentCount();++i) {
\r
540 Field field = ci.fields[i];
\r
541 Annotation[] annotations = getFieldAnnotations(field);
\r
542 Class<?> fieldClass = field.getType();
\r
543 BindingRequest componentRequest = new BindingRequest(fieldClass, annotations);
\r
545 Name nameAnnotation = componentRequest.getAnnotation( Name.class );
\r
546 String fieldName = nameAnnotation!=null ? nameAnnotation.value() : field.getName();
\r
547 Component c = components[i] = new Component(fieldName, null /* updated later */);
\r
549 Identifier idAnnotation = componentRequest.getAnnotation( Identifier.class );
\r
550 if ( idAnnotation!=null ) {
\r
551 componentRequest.dropAnnotations(1, idAnnotation);
\r
552 identifierIndices.add( i );
\r
554 Binding componentBinding = componentBindings[i] = construct( componentRequest );
\r
555 c.type = componentBinding.type();
\r
557 type.setIdentifiers(identifierIndices);
\r
558 inprogress.remove(request);
\r
559 repository.put(request, binding);
\r
564 public Binding construct(BindingRequest request)
\r
565 throws BindingConstructionException
\r
567 if (failures.containsKey(request)) throw failures.get(request);
\r
568 if (inprogress.containsKey(request)) return inprogress.get(request);
\r
569 if (repository.containsRequest(request)) return repository.get(request);
\r
571 // Start construction
\r
573 Binding binding = doConstruct(request);
\r
575 // Avoid creating duplicate binding instances
\r
576 // Only check bindings that are complete
\r
577 if (inprogress.isEmpty() || isComplete(binding, new IdentityHashSet<Binding>())) {
\r
578 Binding defaultBinding = defaultBindingFactory.getBinding(binding.type());
\r
579 if (defaultBinding != null && defaultBinding.equals(binding))
\r
580 binding = defaultBinding;
\r
583 repository.put(request, binding);
\r
586 } catch (RangeException e) {
\r
587 inprogress.remove( request );
\r
588 BindingConstructionException bce = new BindingConstructionException( e );
\r
589 failures.put(request, bce);
\r
591 } catch (BindingConstructionException e) {
\r
592 inprogress.remove( request );
\r
593 failures.put(request, e);
\r
595 } catch (Throwable t) {
\r
596 BindingConstructionException bce = new BindingConstructionException( t );
\r
597 inprogress.remove( request );
\r
598 failures.put(request, bce);
\r
603 boolean isComplete(Binding binding, IdentityHashSet<Binding> checked) {
\r
604 for (Binding b : inprogress.values())
\r
605 if (b == binding) return false;
\r
607 if (checked.contains(binding)) return true;
\r
609 if (binding.getComponentCount() > 0) {
\r
610 checked.add(binding);
\r
611 for (int i = 0; i < binding.getComponentCount(); i++) {
\r
612 if (!isComplete(binding.getComponentBinding(i), checked))
\r
621 * Get a binding to a Java Class. Type details can be modified with annotations.
\r
622 * Please see the package org.simantics.databoard.annotations.
\r
625 * The whether the result binding is a completely mutable or not depends on the
\r
626 * provided classes. For instance, fields such as Boolean, Integer, Long
\r
627 * are not mutable, instead MutableBoolean, MutableInteger and MutableLong are.
\r
628 * The length of Object[] is not mutable, but length of List<Object> is. <p>
\r
630 * Note, results are stored with strong reference into the repository assigned
\r
631 * to this factory.<p>
\r
633 * @see ClassBindingFactory
\r
636 * @throws BindingConstructionException
\r
638 @SuppressWarnings("unchecked")
\r
639 public <T extends Binding> T getBinding(Class<?> clazz)
\r
640 throws BindingConstructionException
\r
642 return (T) construct( new BindingRequest(clazz) );
\r
646 * Get a binding to a Java Class. Use this method to acquire class
\r
647 * parameters for a generics class. <p>
\r
651 * Binding binding = getBinding(Map.class, String.class, Integer.class);
\r
652 * Map<String, Integer> map = (Map<String, Integer>) binding.createDefault();
\r
656 * Binding d = getBinding(List.class, Integer.class);
\r
657 * List<Integer> list = (List<Integer>) d.createRandom(5);
\r
661 * Binding d = getBinding(List.class, List.class, Integer.class);
\r
662 * List<List<Integer>> list = (List<List<Integer>>) d.createRandom(5);
\r
664 * @see ClassBindingFactory
\r
667 * @throws BindingConstructionException
\r
669 @SuppressWarnings("unchecked")
\r
670 public <T extends Binding> T getBinding(Class<?> clazz, Class<?>...parameters)
\r
671 throws BindingConstructionException
\r
673 Arguments args = new ArgumentImpl(parameters);
\r
674 BindingRequest request = new BindingRequest( clazz, args );
\r
675 return (T) construct( request );
\r
678 public Binding getBinding(BindingRequest request) throws BindingConstructionException {
\r
679 return construct(request);
\r
682 public Binding getBindingUnchecked(BindingRequest request) throws RuntimeBindingConstructionException {
\r
684 return construct(request);
\r
685 } catch (BindingConstructionException e) {
\r
686 throw new RuntimeBindingConstructionException(e);
\r
690 static Class<?>[] NO_CLASSES = new Class<?>[0];
\r
692 public static Annotation[] getFieldAnnotations(Field field)
\r
694 Annotation[] annotations = field.getAnnotations().clone();
\r
695 ArrayList<Class<?>> list = new ArrayList<Class<?>>();
\r
696 getTypes( field.getGenericType(), list );
\r
697 Class<?> fieldClass = list.remove(0);
\r
698 Class<?>[] parameterClasses = list.isEmpty() ? NO_CLASSES : list.toArray( NO_CLASSES );
\r
700 if (Set.class.isAssignableFrom(fieldClass) && parameterClasses!=null &¶meterClasses.length==1) {
\r
701 Annotation[] a2 = new Annotation[annotations.length+1];
\r
702 System.arraycopy(annotations, 0, a2, 0, annotations.length);
\r
704 Class<?> keyType = parameterClasses[0];
\r
705 a2[annotations.length] = new ArgumentImpl(keyType);
\r
708 if (Map.class.isAssignableFrom(fieldClass) && parameterClasses!=null && parameterClasses.length==2) {
\r
709 Annotation[] a2 = new Annotation[annotations.length+1];
\r
710 System.arraycopy(annotations, 0, a2, 0, annotations.length);
\r
712 Class<?> keyType = parameterClasses[0];
\r
713 Class<?> valueType = parameterClasses[1];
\r
714 a2[annotations.length] = new ArgumentImpl(keyType, valueType);
\r
717 if (List.class.isAssignableFrom(fieldClass) && parameterClasses!=null && parameterClasses.length==1) {
\r
718 Annotation[] a2 = new Annotation[annotations.length+1];
\r
719 System.arraycopy(annotations, 0, a2, 0, annotations.length);
\r
720 Class<?> componentType = parameterClasses[0];
\r
721 a2[annotations.length] = new ArgumentImpl(componentType);
\r
725 if (parameterClasses!=null && parameterClasses.length>0) {
\r
726 Annotation[] a2 = new Annotation[annotations.length+1];
\r
727 System.arraycopy(annotations, 0, a2, 0, annotations.length);
\r
728 a2[annotations.length] = new ArgumentImpl(parameterClasses);
\r
732 return annotations;
\r
735 static void getTypes(Type type, Collection<Class<?>> result)
\r
737 if ( type instanceof Class ) {
\r
738 result.add( (Class<?>) type );
\r
740 if ( type instanceof ParameterizedType ) {
\r
741 ParameterizedType p = (ParameterizedType) type;
\r
742 getTypes( p.getRawType(), result );
\r
743 for ( Type x : p.getActualTypeArguments() ) getTypes(x, result);
\r
745 if ( type instanceof GenericArrayType) {
\r
746 GenericArrayType at = (GenericArrayType) type;
\r
747 Type componentType = at.getGenericComponentType();
\r
748 ArrayList<Class<?>> list = new ArrayList<Class<?>>(1);
\r
749 getTypes( componentType, list );
\r
751 Object dummy = Array.newInstance(list.get(0), 0);
\r
752 result.add( dummy.getClass() );
\r
753 } else if ( type instanceof TypeVariable ) {
\r
754 result.add( Object.class );
\r
756 throw new RuntimeException( type.getClass()+ " is not implemented" );
\r
760 * Get all actual parameters types, incl. classes and generic types
\r
765 static Type[] getParameterTypes(Field f) {
\r
766 Type t = f.getGenericType();
\r
767 if (t==null || t instanceof ParameterizedType==false) return new Class[0];
\r
768 ParameterizedType p = (ParameterizedType) t;
\r
769 return p.getActualTypeArguments();
\r