-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[] componentRequests;\r
- public Binding[] componentBindings;\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
+/*******************************************************************************
+ * Copyright (c) 2007, 2018 Association for Decentralized Information Management in
+ * Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ * Semantum Oy - gitlab #82
+ *******************************************************************************/
+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 java.util.Map;
+import java.util.WeakHashMap;
+
+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 {
+
+ /**
+ * A weak cache for signature strings by Class.
+ * Prevents the system from constructing new strings
+ * from Classes for every non-trivial BindingRequest.
+ */
+ private static final Map<Class<?>, String> signatureCache = Collections.<Class<?>, String>synchronizedMap(new WeakHashMap<>());
+
+ public static final Annotation[] NO_ANNOTATIONS = {};
+
+ 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 String className; // eg. java.util.Map
+ public final String signature; // eg. Ljava/util/Map;
+ public final String descriptor; //eg. Ljava/util/Map<I;I>;
+
+ 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)
+ {
+ this(clazz, NO_ANNOTATIONS);
+ }
+
+ /**
+ * 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<Class<?>> 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<Class<?>> 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<genericCount; i++)
+ {
+ Class<?> gc = classes.get( pos.value++ );
+ _buildDescriptor(sb, gc, classes, pos);
+ }
+ sb.append('>');
+ sb.append(';');
+ } else {
+ sb.append( getSignature(c) );
+ }
+ }
+
+ public BindingRequest(Class<?> clazz, List<Annotation> 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 extends Annotation> A getAnnotation(Class<A> 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<Annotation> result = new ArrayList<Annotation>( 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; i<annotations.length; i++) {
+ Annotation a = annotations[i];
+ if (i>0) 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";
+ String cached = signatureCache.get(clazz);
+ if (cached == null) {
+ cached = clazz.isArray()
+ ? clazz.getName().replace('.', '/')
+ : "L"+clazz.getName().replace('.', '/')+";";
+ signatureCache.put(clazz, cached);
+ //System.out.println("BindingRequest.getSignature: cache miss for " + clazz + " = " + cached);
+ } else {
+ //System.out.println("BindingRequest.getSignature: cache hit for " + clazz + " = " + cached);
+ }
+ return cached;
+ }
+
+ @SuppressWarnings("unchecked")
+ List<Class<?>> createArgsList()
+ {
+ if (annotations==null || !hasAnnotation(Arguments.class)) return Collections.EMPTY_LIST;
+ List<Class<?>> result = new ArrayList<Class<?>>();
+ for (Annotation a : annotations) {
+ if ( a instanceof Arguments ) {
+ Arguments args = (Arguments) a;
+ for (Class<?> clazz : args.value()) result.add( clazz );
+ }
+ }
+ return result;
+ }
+
+}