package org.simantics.databoard.binding.reflection; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.simantics.databoard.annotations.ArgumentImpl; import org.simantics.databoard.annotations.Arguments; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.primitives.MutableInteger; public class BindingRequest { public static BindingRequest create( Field field ) { Annotation[] annotations = ClassBindingFactory.getFieldAnnotations(field); Class fieldClass = field.getType(); return new BindingRequest(fieldClass, annotations); } /** Requested class */ private Class clazz; private ClassLoader cl; /** Annotations */ public final Annotation[] annotations; public final Annotation[] NO_ANNOTATIONS = new Annotation[0]; public final String className; // eg. java.util.Map public final String signature; // eg. Ljava/util/Map; public final String descriptor; //eg. Ljava/util/Map; public BindingRequest[] componentRequests; public Binding[] componentBindings; transient int hash; /** * Create BindingRequest that creates class lazily. * * @param cl classloader * @param className * @param classSignature * @param classDescriptor * @param annotations */ public BindingRequest(ClassLoader cl, String className, String classSignature, String classDescriptor, Annotation...annotations) { this.className = className; this.cl = cl; this.signature = classSignature; this.annotations = annotations; this.descriptor = classDescriptor; hash = className.hashCode(); for (Annotation a : annotations) { hash = 7*hash + a.hashCode(); } } /** * Create BindingRequest * * @param clazz * @param annotations */ public BindingRequest(Class clazz, Annotation...annotations) { assert annotations!=null; this.clazz = clazz; Annotation[] classAnnotations = clazz.getAnnotations(); if (classAnnotations!=null && classAnnotations.length>0) { this.annotations = new Annotation[classAnnotations.length + annotations.length]; System.arraycopy(annotations, 0, this.annotations, 0, annotations.length); System.arraycopy(classAnnotations, 0, this.annotations, annotations.length, classAnnotations.length); } else { this.annotations = annotations; } className = clazz.getCanonicalName(); signature = getSignature(clazz); List> args = createArgsList(); StringBuilder desc = new StringBuilder(); _buildDescriptor(desc, clazz, args, new MutableInteger(0)); descriptor = desc.toString(); hash = clazz.getName().hashCode(); for (Annotation a : annotations) { hash = 7*hash + a.hashCode(); } } private void _buildDescriptor(StringBuilder sb, Class c, List> classes, MutableInteger pos) { int genericCount = c.getTypeParameters().length; int genericsLeft = classes.size()-pos.value; if ( genericCount>0 && genericsLeft >= genericCount ) { sb.append('L'); sb.append(c.getName().replaceAll("\\.", "/")); sb.append('<'); for (int i=0; i gc = classes.get( pos.value++ ); _buildDescriptor(sb, gc, classes, pos); } sb.append('>'); sb.append(';'); } else { sb.append( getSignature(c) ); } } public BindingRequest(Class clazz, List annotations) { this(clazz, annotations.toArray(new Annotation[annotations.size()])); } public BindingRequest(Class clazz, Class[] parameters) { this(clazz, new ArgumentImpl(parameters)); } public boolean hasAnnotation(Class annotationClass) { for (Annotation a : annotations) if (annotationClass.equals(a.annotationType())) return true; return false; } @SuppressWarnings("unchecked") public A getAnnotation(Class annotationClass) { for (Annotation a : annotations) { if (annotationClass.equals(a.annotationType())) return (A) a; } return null; } @Override public int hashCode() { return hash; } @Override public boolean equals(Object obj) { if (obj==null) return false; if (obj instanceof BindingRequest==false) return false; BindingRequest other = (BindingRequest) obj; return other.descriptor.equals(descriptor) && Arrays.deepEquals(annotations, other.annotations); } public Class getClazz() { if ( clazz==null ) { try { clazz = cl.loadClass( className ); } catch (ClassNotFoundException e) { throw new RuntimeException( e ); } } return clazz; } /** * Return a version of annotations list, where given set of annotations and * a number of class arguments were dropped. * * @param argumentsToDrop the number of class arguments to drop * @param annotationsToDrop annotation to drop * @return request without argument annotation */ public Annotation[] dropAnnotations(int argumentsToDrop, Annotation...annotationsToDrop) { ArrayList result = new ArrayList( annotations.length ); nextA: for (Annotation a : annotations) { for (Annotation b : annotationsToDrop) if (a==b) continue nextA; if (a instanceof Arguments && argumentsToDrop>0) { Arguments c = ArgumentImpl.dropArguments((Arguments) a, argumentsToDrop); if (c!=null) result.add(c); } else result.add(a); } Annotation[] newAnnotations = result.toArray( new Annotation[result.size()] ); return newAnnotations; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(clazz.getName()); if ( annotations!=null && annotations.length>0 ) { sb.append('('); for (int i=0; i0) sb.append(", "); sb.append(a); } sb.append(')'); } return sb.toString(); } /** * Get signature, e.g. Ljava/util/Map; * * @return singature string */ public static String getSignature(Class clazz) { if (clazz==void.class) return "V"; if (clazz==boolean.class) return "Z"; if (clazz==char.class) return "C"; if (clazz==byte.class) return "B"; if (clazz==short.class) return "S"; if (clazz==int.class) return "I"; if (clazz==float.class) return "F"; if (clazz==long.class) return "J"; if (clazz==double.class) return "D"; if (clazz.isArray()) return clazz.getName().replaceAll("\\.", "/"); return "L"+clazz.getName().replaceAll("\\.", "/")+";"; } @SuppressWarnings("unchecked") List> createArgsList() { if (annotations==null || !hasAnnotation(Arguments.class)) return Collections.EMPTY_LIST; List> result = new ArrayList>(); for (Annotation a : annotations) { if ( a instanceof Arguments ) { Arguments args = (Arguments) a; for (Class clazz : args.value()) result.add( clazz ); } } return result; } }