1 /*******************************************************************************
2 * Copyright (c) 2007, 2018 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 * Semantum Oy - gitlab #82, gitlab #313
12 *******************************************************************************/
13 package org.simantics.databoard.binding.reflection;
15 import java.lang.annotation.Annotation;
16 import java.lang.reflect.Field;
17 import java.lang.reflect.Method;
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collections;
21 import java.util.List;
23 import java.util.WeakHashMap;
25 import org.simantics.databoard.annotations.ArgumentImpl;
26 import org.simantics.databoard.annotations.Arguments;
27 import org.simantics.databoard.binding.Binding;
28 import org.simantics.databoard.primitives.MutableInteger;
30 public class BindingRequest {
33 * A weak cache for signature strings by Class.
34 * Prevents the system from constructing new strings
35 * from Classes for every non-trivial BindingRequest.
37 private static final Map<Class<?>, String> signatureCache = Collections.<Class<?>, String>synchronizedMap(new WeakHashMap<>());
39 public static final Annotation[] NO_ANNOTATIONS = {};
41 public static BindingRequest create( Field field )
43 Annotation[] annotations = ClassBindingFactory.getFieldAnnotations(field);
44 Class<?> fieldClass = field.getType();
45 return new BindingRequest(fieldClass, annotations);
48 public static BindingRequest create( Method method )
50 Annotation[] annotations = ClassBindingFactory.getMethodAnnotations(method);
51 Class<?> valueClass = method.getReturnType();
52 return new BindingRequest(valueClass, annotations);
55 /** Requested class */
56 private Class<?> clazz;
57 private ClassLoader cl;
60 public final Annotation[] annotations;
62 public final String className; // eg. java.util.Map
63 public final String signature; // eg. Ljava/util/Map;
64 public final String descriptor; //eg. Ljava/util/Map<I;I>;
66 public BindingRequest[] componentRequests;
67 public Binding[] componentBindings;
72 * Cloning constructor with replacement annotations.
74 * @param other the request to clone
75 * @param annotations the annotations to use while cloning
77 private BindingRequest(BindingRequest other, Annotation...annotations)
79 this.clazz = other.clazz;
81 this.annotations = annotations;
82 this.className = other.className;
83 this.signature = other.signature;
84 this.descriptor = other.descriptor;
85 hash = calcHash(clazz.getName());
89 * Create BindingRequest that creates class lazily.
91 * @param cl classloader
93 * @param classSignature
94 * @param classDescriptor
97 public BindingRequest(ClassLoader cl, String className, String classSignature, String classDescriptor, Annotation...annotations)
99 this.className = className;
101 this.signature = classSignature;
102 this.annotations = annotations;
103 this.descriptor = classDescriptor;
104 hash = calcHash(className);
108 * Create BindingRequest
113 public BindingRequest(Class<?> clazz)
115 this(clazz, NO_ANNOTATIONS);
119 * Create BindingRequest
124 public BindingRequest(Class<?> clazz, Annotation...annotations)
126 assert annotations!=null;
128 Annotation[] classAnnotations = clazz.getAnnotations();
129 if (classAnnotations!=null && classAnnotations.length>0) {
130 this.annotations = new Annotation[classAnnotations.length + annotations.length];
131 System.arraycopy(annotations, 0, this.annotations, 0, annotations.length);
132 System.arraycopy(classAnnotations, 0, this.annotations, annotations.length, classAnnotations.length);
134 this.annotations = annotations;
137 className = clazz.getCanonicalName();
138 signature = getSignature(clazz);
139 descriptor = _buildDescriptor(new StringBuilder(), clazz, createArgsList(), new MutableInteger(0)).toString();
140 hash = calcHash(clazz.getName());
143 public BindingRequest withAnnotations(Annotation... newAnnotations) {
144 return new BindingRequest(this, newAnnotations);
147 private int calcHash(String className) {
148 int hash = className.hashCode();
149 for (Annotation a : this.annotations) {
150 hash += a.hashCode();
155 private StringBuilder _buildDescriptor(StringBuilder sb, Class<?> c, List<Class<?>> classes, MutableInteger pos)
157 int genericCount = c.getTypeParameters().length;
158 int genericsLeft = classes.size()-pos.value;
159 if ( genericCount>0 && genericsLeft >= genericCount ) {
161 sb.append(c.getName().replaceAll("\\.", "/"));
163 for (int i=0; i<genericCount; i++)
165 Class<?> gc = classes.get( pos.value++ );
166 _buildDescriptor(sb, gc, classes, pos);
171 sb.append( getSignature(c) );
176 public BindingRequest(Class<?> clazz, List<Annotation> annotations)
178 this(clazz, annotations.toArray(new Annotation[annotations.size()]));
181 public BindingRequest(Class<?> clazz, Class<?>[] parameters)
183 this(clazz, new ArgumentImpl(parameters));
186 public boolean hasAnnotation(Class<?> annotationClass)
188 for (Annotation a : annotations)
189 if (annotationClass.equals(a.annotationType())) return true;
193 @SuppressWarnings("unchecked")
194 public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
196 for (Annotation a : annotations)
198 if (annotationClass.equals(a.annotationType()))
204 public int hashCode() {
209 public boolean equals(Object obj) {
210 if (obj==null) return false;
211 if (obj instanceof BindingRequest==false) return false;
212 BindingRequest other = (BindingRequest) obj;
213 return other.descriptor.equals(descriptor) &&
214 Arrays.deepEquals(annotations, other.annotations);
217 public Class<?> getClazz()
221 clazz = cl.loadClass( className );
222 } catch (ClassNotFoundException e) {
223 throw new RuntimeException( e );
230 * Return a version of annotations list, where given set of annotations and
231 * a number of class arguments were dropped.
233 * @param argumentsToDrop the number of class arguments to drop
234 * @param annotationsToDrop annotation to drop
235 * @return request without argument annotation
237 public Annotation[] dropAnnotations(int argumentsToDrop, Annotation...annotationsToDrop)
239 ArrayList<Annotation> result = new ArrayList<Annotation>( annotations.length );
241 for (Annotation a : annotations) {
242 for (Annotation b : annotationsToDrop)
243 if (a==b) continue nextA;
244 if (a instanceof Arguments && argumentsToDrop>0) {
245 Arguments c = ArgumentImpl.dropArguments((Arguments) a, argumentsToDrop);
246 if (c!=null) result.add(c);
247 } else result.add(a);
249 Annotation[] newAnnotations = result.toArray( new Annotation[result.size()] );
250 return newAnnotations;
255 public String toString() {
256 StringBuilder sb = new StringBuilder();
257 sb.append(clazz.getName());
258 if ( annotations!=null && annotations.length>0 ) {
260 for (int i=0; i<annotations.length; i++) {
261 Annotation a = annotations[i];
262 if (i>0) sb.append(", ");
268 return sb.toString();
272 * Get signature, e.g. Ljava/util/Map;
274 * @return singature string
276 public static String getSignature(Class<?> clazz) {
277 if (clazz==void.class) return "V";
278 if (clazz==boolean.class) return "Z";
279 if (clazz==char.class) return "C";
280 if (clazz==byte.class) return "B";
281 if (clazz==short.class) return "S";
282 if (clazz==int.class) return "I";
283 if (clazz==float.class) return "F";
284 if (clazz==long.class) return "J";
285 if (clazz==double.class) return "D";
286 String cached = signatureCache.get(clazz);
287 if (cached == null) {
288 cached = clazz.isArray()
289 ? clazz.getName().replace('.', '/')
290 : "L"+clazz.getName().replace('.', '/')+";";
291 signatureCache.put(clazz, cached);
292 //System.out.println("BindingRequest.getSignature: cache miss for " + clazz + " = " + cached);
294 //System.out.println("BindingRequest.getSignature: cache hit for " + clazz + " = " + cached);
299 @SuppressWarnings("unchecked")
300 List<Class<?>> createArgsList()
302 if (annotations==null || !hasAnnotation(Arguments.class)) return Collections.EMPTY_LIST;
303 List<Class<?>> result = new ArrayList<Class<?>>();
304 for (Annotation a : annotations) {
305 if ( a instanceof Arguments ) {
306 Arguments args = (Arguments) a;
307 for (Class<?> clazz : args.value()) result.add( clazz );