X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.databoard%2Fsrc%2Forg%2Fsimantics%2Fdataboard%2Fbinding%2Freflection%2FBindingRequest.java;h=51feb39c3815861e4edde721cb4970ddf33898c0;hp=56cbe61dd2116bd38101d87b794a677907b56e60;hb=a1696e5257fae039410c924155fdeffc1ce1b3e9;hpb=c08364c64a0bb53c45c052a3e4cea8702bbd69a0 diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/BindingRequest.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/BindingRequest.java index 56cbe61dd..51feb39c3 100644 --- a/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/BindingRequest.java +++ b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/BindingRequest.java @@ -1,242 +1,284 @@ -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; - } - -} +/******************************************************************************* + * 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, String> signatureCache = Collections., 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; + + 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> 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"; + 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> 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; + } + +}