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
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 * Create BindingRequest that creates class lazily.
66 * @param cl classloader
68 * @param classSignature
69 * @param classDescriptor
72 public BindingRequest(ClassLoader cl, String className, String classSignature, String classDescriptor, Annotation...annotations)
74 this.className = className;
76 this.signature = classSignature;
77 this.annotations = annotations;
78 this.descriptor = classDescriptor;
79 hash = className.hashCode();
80 for (Annotation a : annotations) {
81 hash = 7*hash + a.hashCode();
86 * Create BindingRequest
91 public BindingRequest(Class<?> clazz)
93 this(clazz, NO_ANNOTATIONS);
97 * Create BindingRequest
102 public BindingRequest(Class<?> clazz, Annotation...annotations)
104 assert annotations!=null;
106 Annotation[] classAnnotations = clazz.getAnnotations();
107 if (classAnnotations!=null && classAnnotations.length>0) {
108 this.annotations = new Annotation[classAnnotations.length + annotations.length];
109 System.arraycopy(annotations, 0, this.annotations, 0, annotations.length);
110 System.arraycopy(classAnnotations, 0, this.annotations, annotations.length, classAnnotations.length);
112 this.annotations = annotations;
115 className = clazz.getCanonicalName();
116 signature = getSignature(clazz);
117 List<Class<?>> args = createArgsList();
118 StringBuilder desc = new StringBuilder();
119 _buildDescriptor(desc, clazz, args, new MutableInteger(0));
120 descriptor = desc.toString();
121 hash = clazz.getName().hashCode();
122 for (Annotation a : annotations) {
123 hash = 7*hash + a.hashCode();
127 private void _buildDescriptor(StringBuilder sb, Class<?> c, List<Class<?>> classes, MutableInteger pos)
129 int genericCount = c.getTypeParameters().length;
130 int genericsLeft = classes.size()-pos.value;
131 if ( genericCount>0 && genericsLeft >= genericCount ) {
133 sb.append(c.getName().replaceAll("\\.", "/"));
135 for (int i=0; i<genericCount; i++)
137 Class<?> gc = classes.get( pos.value++ );
138 _buildDescriptor(sb, gc, classes, pos);
143 sb.append( getSignature(c) );
147 public BindingRequest(Class<?> clazz, List<Annotation> annotations)
149 this(clazz, annotations.toArray(new Annotation[annotations.size()]));
152 public BindingRequest(Class<?> clazz, Class<?>[] parameters)
154 this(clazz, new ArgumentImpl(parameters));
157 public boolean hasAnnotation(Class<?> annotationClass)
159 for (Annotation a : annotations)
160 if (annotationClass.equals(a.annotationType())) return true;
164 @SuppressWarnings("unchecked")
165 public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
167 for (Annotation a : annotations)
169 if (annotationClass.equals(a.annotationType()))
175 public int hashCode() {
180 public boolean equals(Object obj) {
181 if (obj==null) return false;
182 if (obj instanceof BindingRequest==false) return false;
183 BindingRequest other = (BindingRequest) obj;
184 return other.descriptor.equals(descriptor) &&
185 Arrays.deepEquals(annotations, other.annotations);
188 public Class<?> getClazz()
192 clazz = cl.loadClass( className );
193 } catch (ClassNotFoundException e) {
194 throw new RuntimeException( e );
201 * Return a version of annotations list, where given set of annotations and
202 * a number of class arguments were dropped.
204 * @param argumentsToDrop the number of class arguments to drop
205 * @param annotationsToDrop annotation to drop
206 * @return request without argument annotation
208 public Annotation[] dropAnnotations(int argumentsToDrop, Annotation...annotationsToDrop)
210 ArrayList<Annotation> result = new ArrayList<Annotation>( annotations.length );
212 for (Annotation a : annotations) {
213 for (Annotation b : annotationsToDrop)
214 if (a==b) continue nextA;
215 if (a instanceof Arguments && argumentsToDrop>0) {
216 Arguments c = ArgumentImpl.dropArguments((Arguments) a, argumentsToDrop);
217 if (c!=null) result.add(c);
218 } else result.add(a);
220 Annotation[] newAnnotations = result.toArray( new Annotation[result.size()] );
221 return newAnnotations;
226 public String toString() {
227 StringBuilder sb = new StringBuilder();
228 sb.append(clazz.getName());
229 if ( annotations!=null && annotations.length>0 ) {
231 for (int i=0; i<annotations.length; i++) {
232 Annotation a = annotations[i];
233 if (i>0) sb.append(", ");
239 return sb.toString();
243 * Get signature, e.g. Ljava/util/Map;
245 * @return singature string
247 public static String getSignature(Class<?> clazz) {
248 if (clazz==void.class) return "V";
249 if (clazz==boolean.class) return "Z";
250 if (clazz==char.class) return "C";
251 if (clazz==byte.class) return "B";
252 if (clazz==short.class) return "S";
253 if (clazz==int.class) return "I";
254 if (clazz==float.class) return "F";
255 if (clazz==long.class) return "J";
256 if (clazz==double.class) return "D";
257 String cached = signatureCache.get(clazz);
258 if (cached == null) {
259 cached = clazz.isArray()
260 ? clazz.getName().replace('.', '/')
261 : "L"+clazz.getName().replace('.', '/')+";";
262 signatureCache.put(clazz, cached);
263 //System.out.println("BindingRequest.getSignature: cache miss for " + clazz + " = " + cached);
265 //System.out.println("BindingRequest.getSignature: cache hit for " + clazz + " = " + cached);
270 @SuppressWarnings("unchecked")
271 List<Class<?>> createArgsList()
273 if (annotations==null || !hasAnnotation(Arguments.class)) return Collections.EMPTY_LIST;
274 List<Class<?>> result = new ArrayList<Class<?>>();
275 for (Annotation a : annotations) {
276 if ( a instanceof Arguments ) {
277 Arguments args = (Arguments) a;
278 for (Class<?> clazz : args.value()) result.add( clazz );