]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/method/MethodReflectionBinding.java
Improved Bindings.getBinding(Class) caching for Datatype.class
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / method / MethodReflectionBinding.java
1 /*******************************************************************************
2  *  Copyright (c) 2010 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  *******************************************************************************/
12 package org.simantics.databoard.method;
13
14 import java.lang.reflect.Array;
15 import java.lang.reflect.Method;
16 import java.util.HashMap;
17
18 import org.simantics.databoard.Bindings;
19 import org.simantics.databoard.Datatypes;
20 import org.simantics.databoard.binding.Binding;
21 import org.simantics.databoard.binding.RecordBinding;
22 import org.simantics.databoard.binding.UnionBinding;
23 import org.simantics.databoard.binding.error.BindingConstructionException;
24 import org.simantics.databoard.binding.error.BindingException;
25 import org.simantics.databoard.binding.error.DatatypeConstructionException;
26 import org.simantics.databoard.binding.impl.OptionalBindingDefault;
27 import org.simantics.databoard.type.Component;
28 import org.simantics.databoard.type.Datatype;
29 import org.simantics.databoard.type.OptionalType;
30 import org.simantics.databoard.type.RecordType;
31 import org.simantics.databoard.type.UnionType;
32
33 public class MethodReflectionBinding {
34
35         private HashMap<Method, MethodTypeDefinition> methodDescriptionCache = 
36                 new HashMap<Method, MethodTypeDefinition>();
37         
38         private HashMap<Method, MethodTypeBinding> methodBindingCache = 
39                 new HashMap<Method, MethodTypeBinding>();
40         
41         private HashMap<Class<?>, MethodTypeBinding[]> interfaceBindingCache =
42                 new HashMap<Class<?>, MethodTypeBinding[]>();
43         
44         private HashMap<Class<?>, Interface> interfaceTypeCache =
45                 new HashMap<Class<?>, Interface>();
46
47     /**
48      * Get method binding of a method. 
49      * Method arguments are wrapped into an Object[].
50      * Throwables in an UnionType. 
51      * 
52      * @param m
53      * @return method bindings
54      * @throws BindingConstructionException 
55      */
56     public synchronized MethodTypeBinding getMethodBinding(Method m) 
57     throws BindingConstructionException
58     {
59         MethodTypeBinding mb = methodBindingCache.get(m);
60         if (mb==null) {
61                 mb = createMethodBinding(m);
62                 methodBindingCache.put(m, mb);
63         }
64         return mb;
65     }
66
67     private MethodTypeBinding createMethodBinding(Method m) 
68     throws BindingConstructionException
69     {
70         try {
71                 MethodTypeDefinition md = getMethodDescription(m);
72                 Class<?> returnClass = m.getReturnType();
73                 Class<?>[] paramClasses = m.getParameterTypes();
74                 Class<?>[] errorClasses = m.getExceptionTypes();
75                 return createMethodBinding(md, paramClasses, returnClass, errorClasses);
76         } catch (DatatypeConstructionException e) {
77                 throw new BindingConstructionException(e);
78         }
79     }
80     
81     private MethodTypeBinding createMethodBinding(MethodTypeDefinition md, Class<?>[] paramClasses, Class<?> returnClass, Class<?>[] errorClasses) 
82     throws BindingConstructionException
83     {
84         RecordBinding requestBinding;
85         Binding responseBinding;
86         UnionBinding errorBinding;
87         
88         responseBinding = Bindings.getBinding(returnClass);
89         
90         // Wrap arguments into Object[]
91 //      if (paramClasses.length==0) requestBinding = getBinding(void.class);
92 //      else 
93         {
94                 ObjectArrayRecordBinding rb = new ObjectArrayRecordBinding();                   
95                         Binding[] cbs = new Binding[paramClasses.length];
96                         rb.setType( md.getType().getRequestType() );
97                         for (int i=0; i<paramClasses.length; i++) {
98                                 Class<?> paramClass = paramClasses[i];
99                                 Binding binding = Bindings.getBinding(paramClass);
100                                 // All arguments are optional
101                                 binding = new OptionalBindingDefault( binding );
102                                 cbs[i] = binding;
103                         }
104                         rb.setComponentBindings(cbs);
105                 requestBinding = rb;
106         }
107         
108 //      if (errorClasses.length==0) errorBinding = getBinding(void.class);
109 //      else 
110         {
111                 Datatype type = md.getType().getErrorType();
112                 ObjectUnionBinding ub = new ObjectUnionBinding(type, errorClasses);     
113                         Binding cbs[] = new Binding[errorClasses.length];
114                 for (int i=0; i<errorClasses.length; i++)
115                 {
116                         cbs[i] = Bindings.getBinding(errorClasses[i]);
117                 }                               
118                         ub.setComponentBindings(cbs); 
119                 errorBinding = ub;
120         }
121         
122         return new MethodTypeBinding(md, requestBinding, responseBinding, errorBinding);
123     }
124
125     /**
126      * Get method description
127      * 
128      * @param m
129      * @return method description
130      * @throws DatatypeConstructionException 
131      */
132     public synchronized MethodTypeDefinition getMethodDescription(Method m) throws DatatypeConstructionException
133     {
134         MethodTypeDefinition md = methodDescriptionCache.get(m);
135         if (md==null) {
136                 md = createMethodDescription(m);
137                 methodDescriptionCache.put(m, md);
138         }
139         return md;
140     }
141     
142     /**
143      * Get method type
144      * 
145      * @param m
146      * @return method type
147      * @throws DatatypeConstructionException 
148      */
149     public synchronized MethodType getMethodType(Method m) throws DatatypeConstructionException
150     {
151         MethodTypeDefinition md = methodDescriptionCache.get(m);
152         if (md==null) {
153                 md = createMethodDescription(m);
154                 methodDescriptionCache.put(m, md);
155         }
156         return md.getType();
157     }
158     
159     // :unsynchronized
160     private MethodTypeDefinition createMethodDescription(Method m) throws DatatypeConstructionException
161     {
162         Class<?> returnClass = m.getReturnType();
163         Class<?>[] paramClasses = m.getParameterTypes();
164         Class<?>[] errorClasses = m.getExceptionTypes();
165         
166         RecordType requestType;
167         Datatype responseType;
168         UnionType errorType;
169         
170         responseType = Datatypes.getDatatype(returnClass);
171         
172 //      if (paramClasses.length==0) requestType = getDataType(void.class);
173 //      else if (paramClasses.length==1) requestType = getDataType(paramClasses[0]);
174 //      else 
175         {               
176                 RecordType rt = new RecordType();
177                 Component[] components = new Component[paramClasses.length];
178                 rt.setReferable( false );
179                 for (int i=0; i<paramClasses.length; i++) {
180                         Datatype paramType = Datatypes.getDatatype( paramClasses[i] );
181                         paramType = new OptionalType(paramType);
182                         components[i] = new Component("arg"+(i+1), paramType);
183                 }
184                 rt.setComponents( components );
185                 requestType = rt;
186         }
187         
188 //      if (errorClasses.length==0) errorType = getDataType(void.class);
189 //      else 
190         {
191                 UnionType ut = new UnionType();
192                 ut.components = new Component[errorClasses.length];
193                 for (int i=0; i<errorClasses.length; i++)
194                         ut.components[i] = new Component(
195                                         errorClasses[i].getSimpleName(),
196                                         Datatypes.getDatatype(errorClasses[i]));
197                 errorType = ut;
198         }
199         
200         MethodType mt = new MethodType(requestType, responseType, errorType); 
201         MethodTypeDefinition md = new MethodTypeDefinition(m.getName(), mt);
202         return md;
203     }
204     
205
206     public synchronized MethodTypeBinding[] getInterfaceBinding(Class<?> interfaze) 
207     throws BindingConstructionException
208     {
209         MethodTypeBinding[] result = interfaceBindingCache.get( interfaze );
210         if (result==null) {
211                 result = createInterfaceBinding(interfaze);
212                 interfaceBindingCache.put(interfaze, result);
213         }
214         return result;
215     }
216     
217     private MethodTypeBinding[] createInterfaceBinding(Class<?> interfaze) 
218     throws BindingConstructionException
219     {
220         Method methods[] = interfaze.getMethods();
221         MethodTypeBinding result[] = new MethodTypeBinding[methods.length];
222         for (int i=0; i<methods.length; i++)
223         {
224                 result[i] = getMethodBinding( methods[i] );
225         }
226         return result;
227     }
228
229     public synchronized Interface getInterfaceType(Class<?> interfaze) 
230     throws BindingConstructionException
231     {
232         Interface result = interfaceTypeCache.get( interfaze );
233         if (result==null) {
234                 result = createInterfaceType(interfaze);
235                 interfaceTypeCache.put(interfaze, result);
236         }
237         return result;
238     }
239     
240     private Interface createInterfaceType(Class<?> interfaze) 
241     throws BindingConstructionException
242     {
243         MethodTypeBinding[] bindings = getInterfaceBinding(interfaze);
244         MethodTypeDefinition defs[] = new MethodTypeDefinition[bindings.length];
245         for (int i=0; i<bindings.length; i++)
246         {
247                 defs[i] = bindings[i].getMethodDefinition();
248         }
249         return new Interface(defs);
250     }
251
252
253         
254         
255 }
256
257 /**
258  * Binds RecordType to Object[]
259  */
260 class ObjectArrayRecordBinding extends RecordBinding {
261         
262         public ObjectArrayRecordBinding() {             
263         }
264         
265         @Override
266         public void setComponentBindings(Binding[] componentBindings) {
267                 super.setComponentBindings(componentBindings);
268         }
269         
270         @Override
271         public Object create(Object... value) throws BindingException {
272                 return value.clone();
273         }
274         @Override
275         public Object getComponent(Object obj, int index)
276         throws BindingException {
277                 return Array.get(obj, index);
278         }
279         @Override
280         public Object createPartial() throws BindingException {
281                 return new Object[componentBindings.length];
282         }
283         @Override
284         public void setComponents(Object obj, Object... value)
285         throws BindingException {
286                 for (int i=0; i<Array.getLength(obj); i++)                                              
287                         Array.set(obj, i, value[i]);
288         }
289         @Override
290         public void setComponent(Object obj, int index, Object value)
291                         throws BindingException {
292                 Array.set(obj, index, value);           
293         }
294         @Override
295         public boolean isInstance(Object obj) {
296                 return obj instanceof Object[];
297         }
298         @Override
299         public void setType(Datatype type) {
300                 super.setType(type);
301         }
302 }
303
304 /**
305  * Binds UnionType to java.lang.Object
306  *
307  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
308  */
309 class ObjectUnionBinding extends UnionBinding {
310         
311         Class<?>[] classes;
312         
313         public ObjectUnionBinding(Datatype type, Class<?>[] classes) {
314                 this.classes = classes;
315                 this.type = type;
316         }
317         
318         @Override
319         public Object create(int tag, Object value)
320         throws BindingException {
321                 return value;
322         }
323     @Override
324     public void setValue(Object union, int tag, Object value)
325         throws BindingException {
326         throw new BindingException("Cannot change the class of an instance");
327     }
328     @Override
329         public int getTag(Object obj) throws BindingException {
330                 for (int i=0; i<classes.length; i++)
331                         if (classes[i].isInstance(obj))
332                                 return i;               
333                 throw new BindingException(obj+" is not an element");
334         }
335         @Override
336         public Object getValue(Object obj) throws BindingException {
337                 return obj;
338         }
339         @Override
340         public boolean isInstance(Object obj) {
341                 for (Class<?> clazz : classes)
342                         if (clazz.isInstance(obj)) 
343                                 return true;
344                 return false;
345         }       
346         
347         @Override
348         public boolean isImmutable() {
349                 return true;
350         }
351         
352         @Override
353         protected boolean baseEquals( Object obj ) {
354             return super.baseEquals( obj ) && classes.equals( ((ObjectUnionBinding)obj).classes );
355         }
356         
357         @Override
358         protected int baseHashCode() {
359             return super.baseHashCode() + 27 * classes.hashCode();
360         }
361 }