]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/BindingRequest.java
Improved Bindings.getBinding(Class) caching for Datatype.class
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / reflection / BindingRequest.java
index 56cbe61dd2116bd38101d87b794a677907b56e60..51feb39c3815861e4edde721cb4970ddf33898c0 100644 (file)
-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;
+    }
+        
+}