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