]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/BindingRequest.java
Use type reflection tools from databoard in objmap2.
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / reflection / BindingRequest.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2018 Association for Decentralized Information Management in
3  * Industry THTH ry.
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
8  *
9  * Contributors:
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;
14
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;
22 import java.util.Map;
23 import java.util.WeakHashMap;
24
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;
29
30 public class BindingRequest {
31
32     /**
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. 
36      */
37     private static final Map<Class<?>, String> signatureCache = Collections.<Class<?>, String>synchronizedMap(new WeakHashMap<>());
38
39     public static final Annotation[] NO_ANNOTATIONS = {};
40
41         public static BindingRequest create( Field field )
42         {
43         Annotation[] annotations = ClassBindingFactory.getFieldAnnotations(field);
44         Class<?> fieldClass = field.getType(); 
45         return new BindingRequest(fieldClass, annotations);
46         }
47         
48     public static BindingRequest create( Method method )
49     {
50         Annotation[] annotations = ClassBindingFactory.getMethodAnnotations(method);
51         Class<?> valueClass = method.getReturnType(); 
52         return new BindingRequest(valueClass, annotations);
53     }
54         
55         /** Requested class */
56     private Class<?> clazz;
57     private ClassLoader cl;
58     
59     /** Annotations */
60     public final Annotation[] annotations;
61     
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>;
65     
66     public BindingRequest[] componentRequests;
67     public Binding[] componentBindings;
68     
69     transient int hash;
70
71     /**
72      * Cloning constructor with replacement annotations.
73      * 
74      * @param other the request to clone
75      * @param annotations the annotations to use while cloning
76      */
77     private BindingRequest(BindingRequest other, Annotation...annotations)
78     {
79         this.clazz = other.clazz;
80         this.cl = other.cl;
81         this.annotations = annotations;
82         this.className = other.className;
83         this.signature = other.signature;
84         this.descriptor = other.descriptor;
85         hash = calcHash(clazz.getName());
86     }
87
88     /**
89      * Create BindingRequest that creates class lazily. 
90      * 
91      * @param cl classloader
92      * @param className 
93      * @param classSignature 
94      * @param classDescriptor
95      * @param annotations 
96      */
97     public BindingRequest(ClassLoader cl, String className, String classSignature, String classDescriptor, Annotation...annotations)
98     {
99         this.className = className;
100         this.cl = cl;    
101         this.signature = classSignature;
102         this.annotations = annotations;
103         this.descriptor = classDescriptor;
104         hash = calcHash(className);
105     }
106
107     /**
108      * Create BindingRequest
109      * 
110      * @param clazz
111      * @param annotations
112      */
113     public BindingRequest(Class<?> clazz)
114     {
115         this(clazz, NO_ANNOTATIONS);
116     }
117
118     /**
119      * Create BindingRequest
120      * 
121      * @param clazz
122      * @param annotations
123      */
124     public BindingRequest(Class<?> clazz, Annotation...annotations)
125     {
126         assert annotations!=null;
127         this.clazz = clazz;
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);
133         } else {
134                 this.annotations = annotations;
135         }
136         
137         className = clazz.getCanonicalName();
138         signature = getSignature(clazz);
139         descriptor = _buildDescriptor(new StringBuilder(), clazz, createArgsList(), new MutableInteger(0)).toString();
140         hash = calcHash(clazz.getName());
141     }
142
143     public BindingRequest withAnnotations(Annotation... newAnnotations) {
144         return new BindingRequest(this, newAnnotations);
145     }
146
147     private int calcHash(String className) {
148         int hash = className.hashCode();
149         for (Annotation a : this.annotations) {
150             hash += a.hashCode();
151         }
152         return hash;
153     }
154
155     private StringBuilder _buildDescriptor(StringBuilder sb, Class<?> c, List<Class<?>> classes, MutableInteger pos)
156     {
157         int genericCount = c.getTypeParameters().length;
158         int genericsLeft = classes.size()-pos.value;
159         if ( genericCount>0 && genericsLeft >= genericCount ) {
160                 sb.append('L');
161                 sb.append(c.getName().replaceAll("\\.", "/"));
162                 sb.append('<');
163                 for (int i=0; i<genericCount; i++) 
164                 {
165                         Class<?> gc = classes.get( pos.value++ );
166                         _buildDescriptor(sb, gc, classes, pos);
167                 }
168                 sb.append('>');                 
169                 sb.append(';');
170         } else {
171                 sb.append( getSignature(c) );
172         }
173         return sb;
174     }
175
176     public BindingRequest(Class<?> clazz, List<Annotation> annotations)
177     {
178         this(clazz, annotations.toArray(new Annotation[annotations.size()]));
179     }
180     
181     public BindingRequest(Class<?> clazz, Class<?>[] parameters)
182     {
183         this(clazz, new ArgumentImpl(parameters));
184     }
185     
186     public boolean hasAnnotation(Class<?> annotationClass) 
187     {
188         for (Annotation a : annotations)
189             if (annotationClass.equals(a.annotationType())) return true;
190         return false;
191     }
192     
193     @SuppressWarnings("unchecked")
194     public <A extends Annotation> A getAnnotation(Class<A> annotationClass) 
195     {
196         for (Annotation a : annotations)
197         {
198             if (annotationClass.equals(a.annotationType()))
199                 return (A) a;
200         }
201         return null;
202     }
203     @Override
204     public int hashCode() {
205         return hash;
206     }
207     
208     @Override
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);
215     }
216     
217     public Class<?> getClazz()
218     {
219         if ( clazz==null ) {
220                 try {
221                                 clazz = cl.loadClass( className );
222                         } catch (ClassNotFoundException e) {
223                                 throw new RuntimeException( e );
224                         }
225         }
226         return clazz;
227     }
228         
229     /**
230      * Return a version of annotations list, where given set of annotations and
231      * a number of class arguments were dropped. 
232      * 
233      * @param argumentsToDrop the number of class arguments to drop
234      * @param annotationsToDrop annotation to drop
235      * @return request without argument annotation
236      */
237     public Annotation[] dropAnnotations(int argumentsToDrop, Annotation...annotationsToDrop)
238     {
239         ArrayList<Annotation> result = new ArrayList<Annotation>( annotations.length );
240         nextA:
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);
248         }
249         Annotation[] newAnnotations = result.toArray( new Annotation[result.size()] );
250         return newAnnotations;
251     }
252     
253     
254     @Override
255     public String toString() {
256         StringBuilder sb = new StringBuilder();
257         sb.append(clazz.getName());
258         if ( annotations!=null && annotations.length>0 ) {
259             sb.append('(');
260             for (int i=0; i<annotations.length; i++) {
261                 Annotation a = annotations[i];
262                 if (i>0) sb.append(", ");
263                 sb.append(a);
264             }           
265             sb.append(')');
266         }
267         
268         return sb.toString();
269     }
270     
271     /**
272      * Get signature, e.g. Ljava/util/Map;
273      * 
274      * @return singature string
275      */
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);
293                 } else {
294                         //System.out.println("BindingRequest.getSignature: cache hit for " + clazz + " = " + cached);
295                 }
296                 return cached;
297     }
298     
299     @SuppressWarnings("unchecked")
300         List<Class<?>> createArgsList()
301     {
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 );
308                 }
309         }
310         return result;
311     }
312         
313 }