--- /dev/null
+package org.simantics.databoard.binding.reflection;\r
+\r
+import java.lang.annotation.Annotation;\r
+import java.lang.reflect.Array;\r
+import java.lang.reflect.Constructor;\r
+import java.lang.reflect.Field;\r
+import java.lang.reflect.GenericArrayType;\r
+import java.lang.reflect.InvocationTargetException;\r
+import java.lang.reflect.Modifier;\r
+import java.lang.reflect.ParameterizedType;\r
+import java.lang.reflect.Type;\r
+import java.lang.reflect.TypeVariable;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import java.util.concurrent.CopyOnWriteArrayList;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.annotations.ArgumentImpl;\r
+import org.simantics.databoard.annotations.Arguments;\r
+import org.simantics.databoard.annotations.Identifier;\r
+import org.simantics.databoard.annotations.Length;\r
+import org.simantics.databoard.annotations.MIMEType;\r
+import org.simantics.databoard.annotations.Name;\r
+import org.simantics.databoard.annotations.Optional;\r
+import org.simantics.databoard.annotations.Pattern;\r
+import org.simantics.databoard.annotations.Range;\r
+import org.simantics.databoard.annotations.Referable;\r
+import org.simantics.databoard.annotations.Union;\r
+import org.simantics.databoard.annotations.Unit;\r
+import org.simantics.databoard.binding.ArrayBinding;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.MapBinding;\r
+import org.simantics.databoard.binding.OptionalBinding;\r
+import org.simantics.databoard.binding.RecordBinding;\r
+import org.simantics.databoard.binding.error.BindingConstructionException;\r
+import org.simantics.databoard.binding.error.RuntimeBindingConstructionException;\r
+import org.simantics.databoard.binding.factory.BindingRepository;\r
+import org.simantics.databoard.binding.factory.DefaultBindingFactory;\r
+import org.simantics.databoard.binding.factory.TypeBindingFactory;\r
+import org.simantics.databoard.binding.impl.ByteBindingDefault;\r
+import org.simantics.databoard.binding.impl.DoubleBindingDefault;\r
+import org.simantics.databoard.binding.impl.FloatBindingDefault;\r
+import org.simantics.databoard.binding.impl.IntegerBindingDefault;\r
+import org.simantics.databoard.binding.impl.LongBindingDefault;\r
+import org.simantics.databoard.binding.impl.OptionalBindingDefault;\r
+import org.simantics.databoard.binding.impl.StringBindingDefault;\r
+import org.simantics.databoard.binding.impl.UnsignedByteBinding;\r
+import org.simantics.databoard.binding.impl.UnsignedIntegerBinding;\r
+import org.simantics.databoard.binding.impl.UnsignedLongBinding;\r
+import org.simantics.databoard.binding.mutable.MutableByteBinding;\r
+import org.simantics.databoard.binding.mutable.MutableDoubleBinding;\r
+import org.simantics.databoard.binding.mutable.MutableFloatBinding;\r
+import org.simantics.databoard.binding.mutable.MutableIntegerBinding;\r
+import org.simantics.databoard.binding.mutable.MutableLongBinding;\r
+import org.simantics.databoard.binding.mutable.MutableStringBinding;\r
+import org.simantics.databoard.primitives.MutableBoolean;\r
+import org.simantics.databoard.primitives.MutableByte;\r
+import org.simantics.databoard.primitives.MutableDouble;\r
+import org.simantics.databoard.primitives.MutableFloat;\r
+import org.simantics.databoard.primitives.MutableInteger;\r
+import org.simantics.databoard.primitives.MutableLong;\r
+import org.simantics.databoard.primitives.MutableString;\r
+import org.simantics.databoard.primitives.UnsignedByte;\r
+import org.simantics.databoard.primitives.UnsignedInteger;\r
+import org.simantics.databoard.primitives.UnsignedLong;\r
+import org.simantics.databoard.type.ArrayType;\r
+import org.simantics.databoard.type.ByteType;\r
+import org.simantics.databoard.type.Component;\r
+import org.simantics.databoard.type.DoubleType;\r
+import org.simantics.databoard.type.FloatType;\r
+import org.simantics.databoard.type.IntegerType;\r
+import org.simantics.databoard.type.LongType;\r
+import org.simantics.databoard.type.OptionalType;\r
+import org.simantics.databoard.type.RecordType;\r
+import org.simantics.databoard.type.StringType;\r
+import org.simantics.databoard.type.UnionType;\r
+import org.simantics.databoard.util.ArrayUtils;\r
+import org.simantics.databoard.util.IdentityHashSet;\r
+import org.simantics.databoard.util.RangeException;\r
+\r
+/**\r
+ * Type Factory constructs data types from reflection requests.\r
+ * Successfully constructed types are placed in the repository that was given \r
+ * at construction time.\r
+ * \r
+ * @author Toni Kalajainen\r
+ */\r
+public class ClassBindingFactory {\r
+ \r
+ /**\r
+ * Map of failed constructions. \r
+ */\r
+ Map<BindingRequest, BindingConstructionException> failures = new HashMap<BindingRequest, BindingConstructionException>();\r
+ \r
+ /**\r
+ * Map that contains in incomplete constructions.\r
+ */\r
+ Map<BindingRequest, Binding> inprogress = new HashMap<BindingRequest, Binding>();\r
+ \r
+ /**\r
+ * Repository where successful constructions are placed. \r
+ */\r
+ BindingRepository repository; \r
+ \r
+ List<BindingProvider> subFactories = new CopyOnWriteArrayList<BindingProvider>();\r
+\r
+ /** Asm based binding factory for Classes, this is used if Asm library is available, else null */\r
+ RecordBindingProvider asmClassBindingFactory;\r
+ \r
+ /** Reflection based binding factory for Classes, this has poorer performance than asm factory */\r
+ RecordBindingProvider refClassBindingFactory;\r
+\r
+ /** Factory for creating default bindings for data types. */\r
+ TypeBindingFactory defaultBindingFactory;\r
+ \r
+ /**\r
+ * Construct a new reflection binding factory \r
+ */\r
+ public ClassBindingFactory() {\r
+ init();\r
+ this.repository = new BindingRepository();\r
+ this.defaultBindingFactory = new DefaultBindingFactory();\r
+ }\r
+ \r
+ /**\r
+ * Construct a new reflection binding factory that places constructed \r
+ * bindings into user given repository.\r
+ * \r
+ * @param repository\r
+ */\r
+ public ClassBindingFactory(BindingRepository repository, TypeBindingFactory defaultBindingFactory) {\r
+ init();\r
+ this.repository = repository;\r
+ this.defaultBindingFactory = defaultBindingFactory;\r
+ }\r
+ \r
+ void init() {\r
+ refClassBindingFactory = new ReflectionBindingProvider();\r
+ try {\r
+ // Check ASM Exists\r
+ Class.forName("org.objectweb.asm.ClassWriter");\r
+ // Create factory\r
+ Class<?> y = Class.forName("org.simantics.databoard.binding.reflection.AsmBindingProvider");\r
+ Constructor<?> c = y.getConstructor();\r
+ asmClassBindingFactory = (RecordBindingProvider) c.newInstance();\r
+ return;\r
+ } catch (ClassNotFoundException e) {\r
+ } catch (InstantiationException e) {\r
+ } catch (IllegalAccessException e) {\r
+ } catch (IllegalArgumentException e) {\r
+ } catch (InvocationTargetException e) {\r
+ } catch (SecurityException e) {\r
+ } catch (NoSuchMethodException e) {\r
+ }\r
+ }\r
+ \r
+ public void addFactory(BindingProvider factory) \r
+ {\r
+ if (!subFactories.contains(factory)) {\r
+ this.subFactories.add(factory);\r
+ }\r
+ }\r
+ \r
+ public void removeFactory(BindingProvider factory) {\r
+ subFactories.remove(factory);\r
+ }\r
+ \r
+ public BindingRepository getRepository() {\r
+ return repository;\r
+ }\r
+ \r
+ /**\r
+ * Constructs a binding to comply to class request.\r
+ * This is the method sub-classes implement. \r
+ * The implementation should use the inprogress -map for construction of \r
+ * bindings that have component types.\r
+ * \r
+ * e.g. \r
+ * inprogress.put(request, notCompletelyConstructedBinding);\r
+ * Binding componentBinding = construct( componentRequest );\r
+ * notCompletelyConstructedBinding.setChild( componentBinding );\r
+ * inprogress.remove(request);\r
+ * \r
+ * try-finally is not needed.\r
+ * \r
+ * @param request\r
+ * @return\r
+ * @throws BindingConstructionException\r
+ * @throws RangeException\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ protected Binding doConstruct(BindingRequest request) throws BindingConstructionException, RangeException\r
+ {\r
+ // Optional\r
+ if(request.hasAnnotation(Optional.class))\r
+ { \r
+ Optional optional = request.getAnnotation(Optional.class); \r
+ Annotation[] newAnnotations = ArrayUtils.dropElements(request.annotations, optional);\r
+ BindingRequest componentRequest = new BindingRequest(request.getClazz(), newAnnotations);\r
+ OptionalType type = new OptionalType();\r
+ OptionalBinding binding = new OptionalBindingDefault(type, null);\r
+ inprogress.put(request, binding);\r
+ binding.componentBinding = construct( componentRequest );\r
+ type.componentType = binding.componentBinding.type();\r
+ inprogress.remove(request);\r
+ \r
+ return binding;\r
+ }\r
+\r
+ \r
+ \r
+ // Primitive\r
+ { \r
+ Range range = request.getAnnotation(Range.class);\r
+ Unit unit = request.getAnnotation(Unit.class);\r
+ \r
+ if (request.getClazz() == Integer.class || request.getClazz() == int.class || request.getClazz() == MutableInteger.class || UnsignedInteger.class.isAssignableFrom(request.getClazz())) {\r
+ Binding binding = null; \r
+ if (range==null && unit==null) {\r
+ if (request.getClazz() == int.class) binding = Bindings.INTEGER;\r
+ if (request.getClazz() == Integer.class) binding = Bindings.INTEGER;\r
+ if (request.getClazz() == MutableInteger.class) binding = Bindings.MUTABLE_INTEGER;\r
+ if (request.getClazz() == UnsignedInteger.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_INTEGER;\r
+ if (request.getClazz() == UnsignedInteger.Immutable.class) binding = Bindings.UNSIGNED_INTEGER;\r
+ } else {\r
+ IntegerType type = new IntegerType();\r
+ type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );\r
+ type.setUnit( unit==null ? null : unit.value() );\r
+\r
+ if (request.getClazz() == int.class) binding = new IntegerBindingDefault(type);\r
+ if (request.getClazz() == Integer.class) binding = new IntegerBindingDefault(type);\r
+ if (request.getClazz() == MutableInteger.class) binding = new MutableIntegerBinding(type);\r
+ if (request.getClazz() == UnsignedInteger.Mutable.class) binding = new UnsignedIntegerBinding.Mutable(type);\r
+ if (request.getClazz() == UnsignedInteger.Immutable.class) binding = new UnsignedIntegerBinding.Immutable(type);\r
+ }\r
+ if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());\r
+ \r
+ repository.put(request, binding);\r
+ return binding;\r
+ }\r
+ \r
+ if (request.getClazz() == Byte.class || request.getClazz() == byte.class || request.getClazz() == MutableByte.class || UnsignedByte.class.isAssignableFrom(request.getClazz())) {\r
+ Binding binding = null; \r
+ if (range==null && unit==null) {\r
+ if (request.getClazz() == byte.class) binding = Bindings.BYTE;\r
+ if (request.getClazz() == Byte.class) binding = Bindings.BYTE;\r
+ if (request.getClazz() == MutableByte.class) binding = Bindings.MUTABLE_BYTE;\r
+ if (request.getClazz() == UnsignedByte.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_BYTE;\r
+ if (request.getClazz() == UnsignedByte.Immutable.class) binding = Bindings.UNSIGNED_BYTE;\r
+ } else {\r
+ ByteType type = new ByteType();\r
+ type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );\r
+ type.setUnit( unit==null ? null : unit.value() );\r
+\r
+ if (request.getClazz() == byte.class) binding = new ByteBindingDefault(type);\r
+ if (request.getClazz() == Byte.class) binding = new ByteBindingDefault(type);\r
+ if (request.getClazz() == MutableByte.class) binding = new MutableByteBinding(type);\r
+ if (request.getClazz() == UnsignedByte.Mutable.class) binding = new UnsignedByteBinding.Mutable(type);\r
+ if (request.getClazz() == UnsignedByte.Immutable.class) binding = new UnsignedByteBinding.Immutable(type);\r
+ }\r
+ if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());\r
+ \r
+ repository.put(request, binding); \r
+ return binding;\r
+ } \r
+ \r
+ if (request.getClazz() == Long.class || request.getClazz() == long.class || request.getClazz() == MutableLong.class || UnsignedLong.class.isAssignableFrom(request.getClazz())) {\r
+ Binding binding = null; \r
+ if (range==null && unit==null) {\r
+ if (request.getClazz() == long.class) binding = Bindings.LONG;\r
+ if (request.getClazz() == Long.class) binding = Bindings.LONG;\r
+ if (request.getClazz() == MutableLong.class) binding = Bindings.MUTABLE_LONG;\r
+ if (request.getClazz() == UnsignedLong.Mutable.class) binding = Bindings.MUTABLE_UNSIGNED_LONG;\r
+ if (request.getClazz() == UnsignedLong.Immutable.class) binding = Bindings.UNSIGNED_LONG;\r
+ } else {\r
+ LongType type = new LongType();\r
+ type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );\r
+ type.setUnit( unit==null ? null : unit.value() );\r
+\r
+ if (request.getClazz() == long.class) binding = new LongBindingDefault(type);\r
+ if (request.getClazz() == Long.class) binding = new LongBindingDefault(type);\r
+ if (request.getClazz() == MutableLong.class) binding = new MutableLongBinding(type);\r
+ if (request.getClazz() == UnsignedLong.Mutable.class) binding = new UnsignedLongBinding.Mutable(type);\r
+ if (request.getClazz() == UnsignedLong.Immutable.class) binding = new UnsignedLongBinding.Immutable(type);\r
+ }\r
+ if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());\r
+ \r
+ repository.put(request, binding); \r
+ return binding;\r
+ }\r
+ \r
+ if (request.getClazz() == Float.class || request.getClazz() == float.class || request.getClazz() == MutableFloat.class) {\r
+ Binding binding = null; \r
+ if (range==null && unit==null) {\r
+ if (request.getClazz() == float.class) binding = Bindings.FLOAT;\r
+ if (request.getClazz() == Float.class) binding = Bindings.FLOAT;\r
+ if (request.getClazz() == MutableFloat.class) binding = Bindings.MUTABLE_FLOAT;\r
+ } else {\r
+ FloatType type = new FloatType();\r
+ type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );\r
+ type.setUnit( unit==null ? null : unit.value() );\r
+\r
+ if (request.getClazz() == float.class) binding = new FloatBindingDefault(type);\r
+ if (request.getClazz() == Float.class) binding = new FloatBindingDefault(type);\r
+ if (request.getClazz() == MutableFloat.class) binding = new MutableFloatBinding(type);\r
+ }\r
+ if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());\r
+ \r
+ repository.put(request, binding); \r
+ return binding;\r
+ }\r
+ \r
+ if (request.getClazz() == Double.class || request.getClazz() == double.class || request.getClazz() == MutableDouble.class) {\r
+ Binding binding = null; \r
+ if (range==null && unit==null) {\r
+ if (request.getClazz() == double.class) binding = Bindings.DOUBLE;\r
+ if (request.getClazz() == Double.class) binding = Bindings.DOUBLE;\r
+ if (request.getClazz() == MutableDouble.class) binding = Bindings.MUTABLE_DOUBLE;\r
+ } else {\r
+ DoubleType type = new DoubleType();\r
+ type.setRange( range==null ? null : org.simantics.databoard.util.Range.valueOf(range.value()) );\r
+ type.setUnit( unit==null ? null : unit.value() );\r
+\r
+ if (request.getClazz() == double.class) binding = new DoubleBindingDefault(type);\r
+ if (request.getClazz() == Double.class) binding = new DoubleBindingDefault(type);\r
+ if (request.getClazz() == MutableDouble.class) binding = new MutableDoubleBinding(type);\r
+ }\r
+ \r
+ repository.put(request, binding); \r
+ return binding;\r
+ }\r
+\r
+ if (request.getClazz() == Boolean.class || request.getClazz() == boolean.class || request.getClazz() == MutableBoolean.class) {\r
+ Binding binding = null;\r
+ if (request.getClazz() == Boolean.class || request.getClazz() == boolean.class) binding = Bindings.BOOLEAN;\r
+ if (request.getClazz() == MutableBoolean.class) binding = Bindings.MUTABLE_BOOLEAN; \r
+ if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());\r
+ \r
+ repository.put(request, binding); \r
+ return binding;\r
+ }\r
+\r
+ if (request.getClazz() == String.class || request.getClazz() == MutableString.class) {\r
+ Length length = request.getAnnotation(Length.class);\r
+ MIMEType mimeType = request.getAnnotation(MIMEType.class);\r
+ Pattern pattern = request.getAnnotation(Pattern.class);\r
+ Binding binding = null;\r
+ if (length==null && mimeType==null && pattern==null) {\r
+ if (request.getClazz() == String.class) binding = Bindings.STRING;\r
+ if (request.getClazz() == MutableString.class) binding = Bindings.MUTABLE_STRING;\r
+ } else {\r
+ StringType type = new StringType();\r
+ type.setLength( length==null ? null : org.simantics.databoard.util.Range.valueOf( length.value()[0] ) );\r
+ type.setMimeType( mimeType==null ? null : mimeType.value() );\r
+ type.setPattern( pattern==null ? null : pattern.value() ); \r
+ if (request.getClazz() == String.class) binding = new StringBindingDefault(type);\r
+ if (request.getClazz() == MutableString.class) binding = new MutableStringBinding(type);\r
+ }\r
+ if (binding==null) throw new BindingConstructionException("Cannot bind to "+request.getClazz().getSimpleName());\r
+ \r
+ repository.put(request, binding); \r
+ return binding;\r
+ }\r
+ }\r
+\r
+ \r
+ // Custom factories\r
+ for (BindingProvider factory : subFactories) {\r
+ Binding result = factory.provideBinding(this, request); \r
+ if (result == null) continue;\r
+\r
+ /// Array\r
+ // If the result was an arraybinding, complete the composite binding\r
+ if (result instanceof ArrayBinding) {\r
+ ArrayBinding binding = (ArrayBinding) result;\r
+ ArrayType type = binding.type();\r
+ Length lengthAnnotation = request.getAnnotation(Length.class);\r
+ Arguments argumentsAnnotation = request.getAnnotation(Arguments.class);\r
+ Annotation[] componentAnnotations = request.dropAnnotations(1, lengthAnnotation); \r
+ Class<?> componentClass = argumentsAnnotation!=null ? argumentsAnnotation.value()[0] : request.getClazz().getComponentType();\r
+\r
+ org.simantics.databoard.util.Range[] lengths = null;\r
+ if (lengthAnnotation!=null) {\r
+ String[] strs = lengthAnnotation.value();\r
+ lengths = new org.simantics.databoard.util.Range[strs.length];\r
+ for (int i=0; i<strs.length; i++)\r
+ lengths[i] = org.simantics.databoard.util.Range.valueOf(strs[i]); \r
+ }\r
+ \r
+ if ( binding.componentBinding==null && request.componentBinding!=null ) binding.componentBinding = request.componentBinding;\r
+ if ( binding.componentBinding == null) {\r
+ BindingRequest componentRequest = request.componentRequest;\r
+ if (componentClass!=null && componentRequest==null) {\r
+ componentRequest = new BindingRequest(componentClass, componentAnnotations); \r
+ }\r
+ if (componentRequest==null) {\r
+ throw new BindingConstructionException("Cannot determine array component type");\r
+ }\r
+ \r
+ inprogress.put(request, binding);\r
+ binding.componentBinding = construct( componentRequest );\r
+ inprogress.remove(request);\r
+ }\r
+ \r
+ type.componentType = binding.componentBinding.type();\r
+ \r
+ // Multi-dimensional Array. Apply range to sub-array-types\r
+ ArrayType t = type;\r
+ if (lengths!=null) {\r
+ for (int i=0; i<lengths.length; i++) {\r
+ t.setLength( lengths[i] );\r
+ if (i<lengths.length-1)\r
+ t = (ArrayType) t.componentType;\r
+ }\r
+ } \r
+ }\r
+ \r
+ /// Map\r
+ // Map Type\r
+ if ( result instanceof MapBinding ) {\r
+ MapBinding binding = (MapBinding) result;\r
+ \r
+ Arguments argumentsAnnotation = request.getAnnotation(Arguments.class);\r
+ Annotation[] componentAnnotations = request.dropAnnotations( 2 );\r
+\r
+ BindingRequest keyRequest = null;\r
+ BindingRequest valueRequest = null;\r
+ \r
+ if (binding.getKeyBinding() == null) {\r
+ Class<?> keyClass = argumentsAnnotation.value()[0];\r
+ if (keyClass==null) {\r
+ throw new BindingConstructionException("Cannot determine key class, use @Arguments annotation");\r
+ }\r
+ keyRequest = new BindingRequest(keyClass, componentAnnotations);\r
+ }\r
+ else {\r
+ binding.type().keyType = binding.getKeyBinding().type(); \r
+ }\r
+ \r
+ if (binding.getValueBinding() == null) {\r
+ Class<?> valueClass = argumentsAnnotation.value()[1];\r
+ if (valueClass==null) {\r
+ throw new BindingConstructionException("Cannot determine value class, use @Arguments annotation");\r
+ }\r
+ valueRequest = new BindingRequest(valueClass, componentAnnotations);\r
+ }\r
+ else {\r
+ binding.type().valueType = binding.getValueBinding().type(); \r
+ }\r
+ \r
+ inprogress.put(request, result);\r
+ if (keyRequest!=null) {\r
+ Binding keyBinding = construct( keyRequest );\r
+ binding.type().keyType = keyBinding.type();\r
+ binding.setKeyBinding(keyBinding);\r
+ }\r
+ if (valueRequest!=null) {\r
+ Binding valueBinding = construct( valueRequest );\r
+ binding.type().valueType = valueBinding.type();\r
+ binding.setValueBinding(valueBinding);\r
+ }\r
+ inprogress.remove(request);\r
+ }\r
+ \r
+ /// Optional\r
+ \r
+ \r
+ /// Union\r
+ \r
+ \r
+ /// Record\r
+ \r
+ // Its complete, store to repository\r
+ repository.put(request, result); \r
+ return result;\r
+ }\r
+ \r
+ \r
+ \r
+ if (request.getClazz().isEnum()) {\r
+ Enum<?>[] enums = (Enum[]) request.getClazz().getEnumConstants();\r
+ UnionType type = new UnionType(); \r
+ type.components = new Component[enums.length];\r
+ for (int i=0; i<enums.length; i++) {\r
+ String name = enums[i].name();\r
+ type.components[i] = new Component(name, new RecordType(false)); \r
+ } \r
+ EnumClassBinding binding = new EnumClassBinding(type, (Class<Enum<?>>) request.getClazz() ); \r
+ repository.put(request, binding);\r
+ return binding;\r
+ } \r
+ \r
+ // Union type\r
+ if(request.hasAnnotation(Union.class)) {\r
+ Union union = request.getAnnotation(Union.class); \r
+ UnionType type = new UnionType();\r
+ UnionClassBinding binding = new UnionClassBinding(type);\r
+ Class<?>[] cases = union.value();\r
+ int count = cases.length; \r
+ Binding[] componentBindings = new Binding[count];\r
+ type.components = new Component[ count ];\r
+ binding.componentClasses = new Class<?>[ count ];\r
+ binding.setComponentBindings(componentBindings);\r
+ \r
+ inprogress.put(request, binding); \r
+ for(int i=0;i<count;++i) {\r
+ binding.componentClasses[i]= cases[i];\r
+ Component component = type.components[i] = new Component(cases[i].getSimpleName(), null /* updated later*/);\r
+ BindingRequest componentRequest = new BindingRequest( cases[i] );\r
+ Binding componentBinding = componentBindings[i] = construct( componentRequest );\r
+ component.type = componentBinding.type(); \r
+ }\r
+ \r
+ inprogress.remove(request);\r
+ repository.put(request, binding);\r
+ return binding;\r
+ }\r
+ \r
+ // Record type\r
+ {\r
+ RecordType type = new RecordType();\r
+ ClassInfo ci = ClassInfo.getInfo( request.getClazz() );\r
+ boolean publicClass = Modifier.isPublic( request.getClazz().getModifiers() ); \r
+ boolean useAsmBinding = asmClassBindingFactory!=null && publicClass;\r
+ RecordBindingProvider f = useAsmBinding ? asmClassBindingFactory : refClassBindingFactory;\r
+ RecordBinding binding = f.provideRecordBinding( request.getClazz(), type);\r
+ int count = ci.fields.length;\r
+ Binding[] componentBindings = binding.getComponentBindings(); \r
+ Component[] components = new Component[ count ];\r
+ type.setReferable( request.getAnnotation(Referable.class) != null );\r
+ type.setComponents( components );\r
+\r
+ inprogress.put(request, binding);\r
+ List<Integer> identifierIndices = new ArrayList<Integer>(1);\r
+ for(int i=0;i<type.getComponentCount();++i) {\r
+ Field field = ci.fields[i];\r
+ Annotation[] annotations = getFieldAnnotations(field);\r
+ Class<?> fieldClass = field.getType(); \r
+ BindingRequest componentRequest = new BindingRequest(fieldClass, annotations);\r
+ \r
+ Name nameAnnotation = componentRequest.getAnnotation( Name.class );\r
+ String fieldName = nameAnnotation!=null ? nameAnnotation.value() : field.getName();\r
+ Component c = components[i] = new Component(fieldName, null /* updated later */);\r
+ \r
+ Identifier idAnnotation = componentRequest.getAnnotation( Identifier.class );\r
+ if ( idAnnotation!=null ) {\r
+ componentRequest.dropAnnotations(1, idAnnotation);\r
+ identifierIndices.add( i );\r
+ }\r
+ Binding componentBinding = componentBindings[i] = construct( componentRequest );\r
+ c.type = componentBinding.type();\r
+ }\r
+ type.setIdentifiers(identifierIndices);\r
+ inprogress.remove(request);\r
+ repository.put(request, binding);\r
+ return binding;\r
+ }\r
+ }\r
+ \r
+ public Binding construct(BindingRequest request) \r
+ throws BindingConstructionException\r
+ {\r
+ if (failures.containsKey(request)) throw failures.get(request);\r
+ if (inprogress.containsKey(request)) return inprogress.get(request);\r
+ if (repository.containsRequest(request)) return repository.get(request);\r
+ \r
+ // Start construction\r
+ try { \r
+ Binding binding = doConstruct(request);\r
+ \r
+ // Avoid creating duplicate binding instances\r
+ // Only check bindings that are complete\r
+ if (inprogress.isEmpty() || isComplete(binding, new IdentityHashSet<Binding>())) {\r
+ Binding defaultBinding = defaultBindingFactory.getBinding(binding.type());\r
+ if (defaultBinding != null && defaultBinding.equals(binding))\r
+ binding = defaultBinding;\r
+ }\r
+ \r
+ repository.put(request, binding);\r
+ \r
+ return binding;\r
+ } catch (RangeException e) {\r
+ inprogress.remove( request );\r
+ BindingConstructionException bce = new BindingConstructionException( e ); \r
+ failures.put(request, bce);\r
+ throw bce;\r
+ } catch (BindingConstructionException e) {\r
+ inprogress.remove( request );\r
+ failures.put(request, e);\r
+ throw e;\r
+ } catch (Throwable t) {\r
+ BindingConstructionException bce = new BindingConstructionException( t );\r
+ inprogress.remove( request );\r
+ failures.put(request, bce);\r
+ throw bce;\r
+ }\r
+ }\r
+ \r
+ boolean isComplete(Binding binding, IdentityHashSet<Binding> checked) {\r
+ for (Binding b : inprogress.values())\r
+ if (b == binding) return false;\r
+ \r
+ if (checked.contains(binding)) return true;\r
+ \r
+ if (binding.getComponentCount() > 0) {\r
+ checked.add(binding);\r
+ for (int i = 0; i < binding.getComponentCount(); i++) {\r
+ if (!isComplete(binding.getComponentBinding(i), checked))\r
+ return false;\r
+ }\r
+ }\r
+ \r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Get a binding to a Java Class. Type details can be modified with annotations.\r
+ * Please see the package org.simantics.databoard.annotations. \r
+ * <p>\r
+ * \r
+ * The whether the result binding is a completely mutable or not depends on the\r
+ * provided classes. For instance, fields such as Boolean, Integer, Long\r
+ * are not mutable, instead MutableBoolean, MutableInteger and MutableLong are.\r
+ * The length of Object[] is not mutable, but length of List<Object> is. <p>\r
+ * \r
+ * Note, results are stored with strong reference into the repository assigned\r
+ * to this factory.<p> \r
+ * \r
+ * @see ClassBindingFactory \r
+ * @param clazz\r
+ * @return binding\r
+ * @throws BindingConstructionException\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ public <T extends Binding> T getBinding(Class<?> clazz)\r
+ throws BindingConstructionException\r
+ {\r
+ return (T) construct( new BindingRequest(clazz) );\r
+ }\r
+ \r
+ /**\r
+ * Get a binding to a Java Class. Use this method to acquire class \r
+ * parameters for a generics class. <p>\r
+ * \r
+ * Example 1: \r
+ * \r
+ * Binding binding = getBinding(Map.class, String.class, Integer.class);\r
+ * Map<String, Integer> map = (Map<String, Integer>) binding.createDefault();\r
+ * \r
+ * Example 2:\r
+ * \r
+ * Binding d = getBinding(List.class, Integer.class);\r
+ * List<Integer> list = (List<Integer>) d.createRandom(5);\r
+ * \r
+ * Example 3:\r
+ * \r
+ * Binding d = getBinding(List.class, List.class, Integer.class);\r
+ * List<List<Integer>> list = (List<List<Integer>>) d.createRandom(5);\r
+ * \r
+ * @see ClassBindingFactory \r
+ * @param clazz\r
+ * @return binding\r
+ * @throws BindingConstructionException\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ public <T extends Binding> T getBinding(Class<?> clazz, Class<?>...parameters)\r
+ throws BindingConstructionException\r
+ {\r
+ Arguments args = new ArgumentImpl(parameters);\r
+ BindingRequest request = new BindingRequest( clazz, args );\r
+ return (T) construct( request );\r
+ } \r
+ \r
+ public Binding getBinding(BindingRequest request) throws BindingConstructionException { \r
+ return construct(request);\r
+ }\r
+ \r
+ public Binding getBindingUnchecked(BindingRequest request) throws RuntimeBindingConstructionException {\r
+ try {\r
+ return construct(request);\r
+ } catch (BindingConstructionException e) {\r
+ throw new RuntimeBindingConstructionException(e);\r
+ }\r
+ } \r
+ \r
+ static Class<?>[] NO_CLASSES = new Class<?>[0];\r
+ \r
+ public static Annotation[] getFieldAnnotations(Field field) \r
+ {\r
+ Annotation[] annotations = field.getAnnotations().clone();\r
+ ArrayList<Class<?>> list = new ArrayList<Class<?>>();\r
+ getTypes( field.getGenericType(), list );\r
+ Class<?> fieldClass = list.remove(0);\r
+ Class<?>[] parameterClasses = list.isEmpty() ? NO_CLASSES : list.toArray( NO_CLASSES );\r
+ \r
+ if (Set.class.isAssignableFrom(fieldClass) && parameterClasses!=null &¶meterClasses.length==1) {\r
+ Annotation[] a2 = new Annotation[annotations.length+1];\r
+ System.arraycopy(annotations, 0, a2, 0, annotations.length);\r
+ \r
+ Class<?> keyType = parameterClasses[0];\r
+ a2[annotations.length] = new ArgumentImpl(keyType); \r
+ annotations = a2;\r
+ } \r
+ if (Map.class.isAssignableFrom(fieldClass) && parameterClasses!=null && parameterClasses.length==2) {\r
+ Annotation[] a2 = new Annotation[annotations.length+1];\r
+ System.arraycopy(annotations, 0, a2, 0, annotations.length);\r
+ \r
+ Class<?> keyType = parameterClasses[0];\r
+ Class<?> valueType = parameterClasses[1];\r
+ a2[annotations.length] = new ArgumentImpl(keyType, valueType); \r
+ annotations = a2;\r
+ } \r
+ if (List.class.isAssignableFrom(fieldClass) && parameterClasses!=null && parameterClasses.length==1) {\r
+ Annotation[] a2 = new Annotation[annotations.length+1];\r
+ System.arraycopy(annotations, 0, a2, 0, annotations.length);\r
+ Class<?> componentType = parameterClasses[0];\r
+ a2[annotations.length] = new ArgumentImpl(componentType);\r
+ annotations = a2;\r
+ } \r
+ \r
+ if (parameterClasses!=null && parameterClasses.length>0) {\r
+ Annotation[] a2 = new Annotation[annotations.length+1];\r
+ System.arraycopy(annotations, 0, a2, 0, annotations.length);\r
+ a2[annotations.length] = new ArgumentImpl(parameterClasses);\r
+ annotations = a2;\r
+ }\r
+ \r
+ return annotations;\r
+ }\r
+ \r
+ static void getTypes(Type type, Collection<Class<?>> result) \r
+ {\r
+ if ( type instanceof Class ) {\r
+ result.add( (Class<?>) type );\r
+ } else \r
+ if ( type instanceof ParameterizedType ) {\r
+ ParameterizedType p = (ParameterizedType) type;\r
+ getTypes( p.getRawType(), result );\r
+ for ( Type x : p.getActualTypeArguments() ) getTypes(x, result);\r
+ } else\r
+ if ( type instanceof GenericArrayType) {\r
+ GenericArrayType at = (GenericArrayType) type;\r
+ Type componentType = at.getGenericComponentType();\r
+ ArrayList<Class<?>> list = new ArrayList<Class<?>>(1);\r
+ getTypes( componentType, list );\r
+ // To Array class\r
+ Object dummy = Array.newInstance(list.get(0), 0);\r
+ result.add( dummy.getClass() ); \r
+ } else if ( type instanceof TypeVariable ) {\r
+ result.add( Object.class ); \r
+ } else\r
+ throw new RuntimeException( type.getClass()+ " is not implemented" );\r
+ }\r
+ \r
+ /**\r
+ * Get all actual parameters types, incl. classes and generic types\r
+ * \r
+ * @param f\r
+ * @return\r
+ */\r
+ static Type[] getParameterTypes(Field f) {\r
+ Type t = f.getGenericType();\r
+ if (t==null || t instanceof ParameterizedType==false) return new Class[0]; \r
+ ParameterizedType p = (ParameterizedType) t;\r
+ return p.getActualTypeArguments();\r
+ }\r
+ \r
+\r
+}\r