1 /*******************************************************************************
2 * Copyright (c) 2010 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 *******************************************************************************/
12 package org.simantics.databoard.method;
14 import java.lang.reflect.Array;
15 import java.lang.reflect.Method;
16 import java.util.HashMap;
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;
33 public class MethodReflectionBinding {
35 private HashMap<Method, MethodTypeDefinition> methodDescriptionCache =
36 new HashMap<Method, MethodTypeDefinition>();
38 private HashMap<Method, MethodTypeBinding> methodBindingCache =
39 new HashMap<Method, MethodTypeBinding>();
41 private HashMap<Class<?>, MethodTypeBinding[]> interfaceBindingCache =
42 new HashMap<Class<?>, MethodTypeBinding[]>();
44 private HashMap<Class<?>, Interface> interfaceTypeCache =
45 new HashMap<Class<?>, Interface>();
48 * Get method binding of a method.
49 * Method arguments are wrapped into an Object[].
50 * Throwables in an UnionType.
53 * @return method bindings
54 * @throws BindingConstructionException
56 public synchronized MethodTypeBinding getMethodBinding(Method m)
57 throws BindingConstructionException
59 MethodTypeBinding mb = methodBindingCache.get(m);
61 mb = createMethodBinding(m);
62 methodBindingCache.put(m, mb);
67 private MethodTypeBinding createMethodBinding(Method m)
68 throws BindingConstructionException
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);
81 private MethodTypeBinding createMethodBinding(MethodTypeDefinition md, Class<?>[] paramClasses, Class<?> returnClass, Class<?>[] errorClasses)
82 throws BindingConstructionException
84 RecordBinding requestBinding;
85 Binding responseBinding;
86 UnionBinding errorBinding;
88 responseBinding = Bindings.getBinding(returnClass);
90 // Wrap arguments into Object[]
91 // if (paramClasses.length==0) requestBinding = getBinding(void.class);
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 );
104 rb.setComponentBindings(cbs);
108 // if (errorClasses.length==0) errorBinding = getBinding(void.class);
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++)
116 cbs[i] = Bindings.getBinding(errorClasses[i]);
118 ub.setComponentBindings(cbs);
122 return new MethodTypeBinding(md, requestBinding, responseBinding, errorBinding);
126 * Get method description
129 * @return method description
130 * @throws DatatypeConstructionException
132 public synchronized MethodTypeDefinition getMethodDescription(Method m) throws DatatypeConstructionException
134 MethodTypeDefinition md = methodDescriptionCache.get(m);
136 md = createMethodDescription(m);
137 methodDescriptionCache.put(m, md);
146 * @return method type
147 * @throws DatatypeConstructionException
149 public synchronized MethodType getMethodType(Method m) throws DatatypeConstructionException
151 MethodTypeDefinition md = methodDescriptionCache.get(m);
153 md = createMethodDescription(m);
154 methodDescriptionCache.put(m, md);
160 private MethodTypeDefinition createMethodDescription(Method m) throws DatatypeConstructionException
162 Class<?> returnClass = m.getReturnType();
163 Class<?>[] paramClasses = m.getParameterTypes();
164 Class<?>[] errorClasses = m.getExceptionTypes();
166 RecordType requestType;
167 Datatype responseType;
170 responseType = Datatypes.getDatatype(returnClass);
172 // if (paramClasses.length==0) requestType = getDataType(void.class);
173 // else if (paramClasses.length==1) requestType = getDataType(paramClasses[0]);
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);
184 rt.setComponents( components );
188 // if (errorClasses.length==0) errorType = getDataType(void.class);
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]));
200 MethodType mt = new MethodType(requestType, responseType, errorType);
201 MethodTypeDefinition md = new MethodTypeDefinition(m.getName(), mt);
206 public synchronized MethodTypeBinding[] getInterfaceBinding(Class<?> interfaze)
207 throws BindingConstructionException
209 MethodTypeBinding[] result = interfaceBindingCache.get( interfaze );
211 result = createInterfaceBinding(interfaze);
212 interfaceBindingCache.put(interfaze, result);
217 private MethodTypeBinding[] createInterfaceBinding(Class<?> interfaze)
218 throws BindingConstructionException
220 Method methods[] = interfaze.getMethods();
221 MethodTypeBinding result[] = new MethodTypeBinding[methods.length];
222 for (int i=0; i<methods.length; i++)
224 result[i] = getMethodBinding( methods[i] );
229 public synchronized Interface getInterfaceType(Class<?> interfaze)
230 throws BindingConstructionException
232 Interface result = interfaceTypeCache.get( interfaze );
234 result = createInterfaceType(interfaze);
235 interfaceTypeCache.put(interfaze, result);
240 private Interface createInterfaceType(Class<?> interfaze)
241 throws BindingConstructionException
243 MethodTypeBinding[] bindings = getInterfaceBinding(interfaze);
244 MethodTypeDefinition defs[] = new MethodTypeDefinition[bindings.length];
245 for (int i=0; i<bindings.length; i++)
247 defs[i] = bindings[i].getMethodDefinition();
249 return new Interface(defs);
258 * Binds RecordType to Object[]
260 class ObjectArrayRecordBinding extends RecordBinding {
262 public ObjectArrayRecordBinding() {
266 public void setComponentBindings(Binding[] componentBindings) {
267 super.setComponentBindings(componentBindings);
271 public Object create(Object... value) throws BindingException {
272 return value.clone();
275 public Object getComponent(Object obj, int index)
276 throws BindingException {
277 return Array.get(obj, index);
280 public Object createPartial() throws BindingException {
281 return new Object[componentBindings.length];
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]);
290 public void setComponent(Object obj, int index, Object value)
291 throws BindingException {
292 Array.set(obj, index, value);
295 public boolean isInstance(Object obj) {
296 return obj instanceof Object[];
299 public void setType(Datatype type) {
305 * Binds UnionType to java.lang.Object
307 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
309 class ObjectUnionBinding extends UnionBinding {
313 public ObjectUnionBinding(Datatype type, Class<?>[] classes) {
314 this.classes = classes;
319 public Object create(int tag, Object value)
320 throws BindingException {
324 public void setValue(Object union, int tag, Object value)
325 throws BindingException {
326 throw new BindingException("Cannot change the class of an instance");
329 public int getTag(Object obj) throws BindingException {
330 for (int i=0; i<classes.length; i++)
331 if (classes[i].isInstance(obj))
333 throw new BindingException(obj+" is not an element");
336 public Object getValue(Object obj) throws BindingException {
340 public boolean isInstance(Object obj) {
341 for (Class<?> clazz : classes)
342 if (clazz.isInstance(obj))
348 public boolean isImmutable() {
353 protected boolean baseEquals( Object obj ) {
354 return super.baseEquals( obj ) && classes.equals( ((ObjectUnionBinding)obj).classes );
358 protected int baseHashCode() {
359 return super.baseHashCode() + 27 * classes.hashCode();