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.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.Collections;
20 import java.util.List;
22 import java.util.WeakHashMap;
24 import org.simantics.databoard.annotations.ArgumentImpl;
25 import org.simantics.databoard.annotations.Arguments;
26 import org.simantics.databoard.binding.Binding;
27 import org.simantics.databoard.primitives.MutableInteger;
29 public class BindingRequest {
32 * A weak cache for signature strings by Class.
33 * Prevents the system from constructing new strings
34 * from Classes for every non-trivial BindingRequest.
36 private static final Map<Class<?>, String> signatureCache = Collections.<Class<?>, String>synchronizedMap(new WeakHashMap<>());
38 public static final Annotation[] NO_ANNOTATIONS = {};
40 public static BindingRequest create( Field field )
42 Annotation[] annotations = ClassBindingFactory.getFieldAnnotations(field);
43 Class<?> fieldClass = field.getType();
44 return new BindingRequest(fieldClass, annotations);
47 /** Requested class */
48 private Class<?> clazz;
49 private ClassLoader cl;
52 public final Annotation[] annotations;
54 public final String className; // eg. java.util.Map
55 public final String signature; // eg. Ljava/util/Map;
56 public final String descriptor; //eg. Ljava/util/Map<I;I>;
58 public BindingRequest[] componentRequests;
59 public Binding[] componentBindings;
64 * Cloning constructor with replacement annotations.
66 * @param other the request to clone
67 * @param annotations the annotations to use while cloning
69 private BindingRequest(BindingRequest other, Annotation...annotations)
71 this.clazz = other.clazz;
73 this.annotations = annotations;
74 this.className = other.className;
75 this.signature = other.signature;
76 this.descriptor = other.descriptor;
77 hash = calcHash(clazz.getName());
81 * Create BindingRequest that creates class lazily.
83 * @param cl classloader
85 * @param classSignature
86 * @param classDescriptor
89 public BindingRequest(ClassLoader cl, String className, String classSignature, String classDescriptor, Annotation...annotations)
91 this.className = className;
93 this.signature = classSignature;
94 this.annotations = annotations;
95 this.descriptor = classDescriptor;
96 hash = calcHash(className);
100 * Create BindingRequest
105 public BindingRequest(Class<?> clazz)
107 this(clazz, NO_ANNOTATIONS);
111 * Create BindingRequest
116 public BindingRequest(Class<?> clazz, Annotation...annotations)
118 assert annotations!=null;
120 Annotation[] classAnnotations = clazz.getAnnotations();
121 if (classAnnotations!=null && classAnnotations.length>0) {
122 this.annotations = new Annotation[classAnnotations.length + annotations.length];
123 System.arraycopy(annotations, 0, this.annotations, 0, annotations.length);
124 System.arraycopy(classAnnotations, 0, this.annotations, annotations.length, classAnnotations.length);
126 this.annotations = annotations;
129 className = clazz.getCanonicalName();
130 signature = getSignature(clazz);
131 descriptor = _buildDescriptor(new StringBuilder(), clazz, createArgsList(), new MutableInteger(0)).toString();
132 hash = calcHash(clazz.getName());
135 public BindingRequest withAnnotations(Annotation... newAnnotations) {
136 return new BindingRequest(this, newAnnotations);
139 private int calcHash(String className) {
140 int hash = className.hashCode();
141 for (Annotation a : this.annotations) {
142 hash += a.hashCode();
147 private StringBuilder _buildDescriptor(StringBuilder sb, Class<?> c, List<Class<?>> classes, MutableInteger pos)
149 int genericCount = c.getTypeParameters().length;
150 int genericsLeft = classes.size()-pos.value;
151 if ( genericCount>0 && genericsLeft >= genericCount ) {
153 sb.append(c.getName().replaceAll("\\.", "/"));
155 for (int i=0; i<genericCount; i++)
157 Class<?> gc = classes.get( pos.value++ );
158 _buildDescriptor(sb, gc, classes, pos);
163 sb.append( getSignature(c) );
168 public BindingRequest(Class<?> clazz, List<Annotation> annotations)
170 this(clazz, annotations.toArray(new Annotation[annotations.size()]));
173 public BindingRequest(Class<?> clazz, Class<?>[] parameters)
175 this(clazz, new ArgumentImpl(parameters));
178 public boolean hasAnnotation(Class<?> annotationClass)
180 for (Annotation a : annotations)
181 if (annotationClass.equals(a.annotationType())) return true;
185 @SuppressWarnings("unchecked")
186 public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
188 for (Annotation a : annotations)
190 if (annotationClass.equals(a.annotationType()))
196 public int hashCode() {
201 public boolean equals(Object obj) {
202 if (obj==null) return false;
203 if (obj instanceof BindingRequest==false) return false;
204 BindingRequest other = (BindingRequest) obj;
205 return other.descriptor.equals(descriptor) &&
206 Arrays.deepEquals(annotations, other.annotations);
209 public Class<?> getClazz()
213 clazz = cl.loadClass( className );
214 } catch (ClassNotFoundException e) {
215 throw new RuntimeException( e );
222 * Return a version of annotations list, where given set of annotations and
223 * a number of class arguments were dropped.
225 * @param argumentsToDrop the number of class arguments to drop
226 * @param annotationsToDrop annotation to drop
227 * @return request without argument annotation
229 public Annotation[] dropAnnotations(int argumentsToDrop, Annotation...annotationsToDrop)
231 ArrayList<Annotation> result = new ArrayList<Annotation>( annotations.length );
233 for (Annotation a : annotations) {
234 for (Annotation b : annotationsToDrop)
235 if (a==b) continue nextA;
236 if (a instanceof Arguments && argumentsToDrop>0) {
237 Arguments c = ArgumentImpl.dropArguments((Arguments) a, argumentsToDrop);
238 if (c!=null) result.add(c);
239 } else result.add(a);
241 Annotation[] newAnnotations = result.toArray( new Annotation[result.size()] );
242 return newAnnotations;
247 public String toString() {
248 StringBuilder sb = new StringBuilder();
249 sb.append(clazz.getName());
250 if ( annotations!=null && annotations.length>0 ) {
252 for (int i=0; i<annotations.length; i++) {
253 Annotation a = annotations[i];
254 if (i>0) sb.append(", ");
260 return sb.toString();
264 * Get signature, e.g. Ljava/util/Map;
266 * @return singature string
268 public static String getSignature(Class<?> clazz) {
269 if (clazz==void.class) return "V";
270 if (clazz==boolean.class) return "Z";
271 if (clazz==char.class) return "C";
272 if (clazz==byte.class) return "B";
273 if (clazz==short.class) return "S";
274 if (clazz==int.class) return "I";
275 if (clazz==float.class) return "F";
276 if (clazz==long.class) return "J";
277 if (clazz==double.class) return "D";
278 String cached = signatureCache.get(clazz);
279 if (cached == null) {
280 cached = clazz.isArray()
281 ? clazz.getName().replace('.', '/')
282 : "L"+clazz.getName().replace('.', '/')+";";
283 signatureCache.put(clazz, cached);
284 //System.out.println("BindingRequest.getSignature: cache miss for " + clazz + " = " + cached);
286 //System.out.println("BindingRequest.getSignature: cache hit for " + clazz + " = " + cached);
291 @SuppressWarnings("unchecked")
292 List<Class<?>> createArgsList()
294 if (annotations==null || !hasAnnotation(Arguments.class)) return Collections.EMPTY_LIST;
295 List<Class<?>> result = new ArrayList<Class<?>>();
296 for (Annotation a : annotations) {
297 if ( a instanceof Arguments ) {
298 Arguments args = (Arguments) a;
299 for (Class<?> clazz : args.value()) result.add( clazz );