]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/ClassBindingFactory.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / reflection / ClassBindingFactory.java
diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/ClassBindingFactory.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/ClassBindingFactory.java
new file mode 100644 (file)
index 0000000..081e8b1
--- /dev/null
@@ -0,0 +1,773 @@
+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 &&parameterClasses.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