X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=bundles%2Forg.simantics.databoard%2Fsrc%2Forg%2Fsimantics%2Fdataboard%2Fbinding%2Freflection%2FClassBindingFactory.java;fp=bundles%2Forg.simantics.databoard%2Fsrc%2Forg%2Fsimantics%2Fdataboard%2Fbinding%2Freflection%2FClassBindingFactory.java;h=081e8b1ecbdb4547f5f1a22c9620db43c8f0d5e1;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git 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 index 000000000..081e8b1ec --- /dev/null +++ b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/ClassBindingFactory.java @@ -0,0 +1,773 @@ +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.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 failures = new HashMap(); + + /** + * Map that contains in incomplete constructions. + */ + Map inprogress = new HashMap(); + + /** + * Repository where successful constructions are placed. + */ + BindingRepository repository; + + List subFactories = new CopyOnWriteArrayList(); + + /** 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 keyClass = argumentsAnnotation.value()[0]; + if (keyClass==null) { + throw new BindingConstructionException("Cannot determine key class, use @Arguments annotation"); + } + keyRequest = new BindingRequest(keyClass, componentAnnotations); + } + else { + binding.type().keyType = binding.getKeyBinding().type(); + } + + if (binding.getValueBinding() == null) { + Class valueClass = argumentsAnnotation.value()[1]; + if (valueClass==null) { + throw new BindingConstructionException("Cannot determine value class, use @Arguments annotation"); + } + valueRequest = new BindingRequest(valueClass, componentAnnotations); + } + else { + binding.type().valueType = binding.getValueBinding().type(); + } + + inprogress.put(request, result); + if (keyRequest!=null) { + Binding keyBinding = construct( keyRequest ); + binding.type().keyType = keyBinding.type(); + binding.setKeyBinding(keyBinding); + } + if (valueRequest!=null) { + Binding valueBinding = construct( valueRequest ); + binding.type().valueType = valueBinding.type(); + binding.setValueBinding(valueBinding); + } + inprogress.remove(request); + } + + /// 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>) 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 identifierIndices = new ArrayList(1); + for(int i=0;i 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 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 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. + *

+ * + * 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 is.

+ * + * Note, results are stored with strong reference into the repository assigned + * to this factory.

+ * + * @see ClassBindingFactory + * @param clazz + * @return binding + * @throws BindingConstructionException + */ + @SuppressWarnings("unchecked") + public 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.

+ * + * Example 1: + * + * Binding binding = getBinding(Map.class, String.class, Integer.class); + * Map map = (Map) binding.createDefault(); + * + * Example 2: + * + * Binding d = getBinding(List.class, Integer.class); + * List list = (List) d.createRandom(5); + * + * Example 3: + * + * Binding d = getBinding(List.class, List.class, Integer.class); + * List> list = (List>) d.createRandom(5); + * + * @see ClassBindingFactory + * @param clazz + * @return binding + * @throws BindingConstructionException + */ + @SuppressWarnings("unchecked") + public 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> list = new ArrayList>(); + 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> 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> list = new ArrayList>(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(); + } + + +}