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