]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/method/MethodReflectionBinding.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / method / MethodReflectionBinding.java
diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/method/MethodReflectionBinding.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/method/MethodReflectionBinding.java
new file mode 100644 (file)
index 0000000..93d3e22
--- /dev/null
@@ -0,0 +1,361 @@
+/*******************************************************************************\r
+ *  Copyright (c) 2010 Association for Decentralized Information Management in\r
+ *  Industry THTH ry.\r
+ *  All rights reserved. This program and the accompanying materials\r
+ *  are made available under the terms of the Eclipse Public License v1.0\r
+ *  which accompanies this distribution, and is available at\r
+ *  http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ *  Contributors:\r
+ *      VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.method;
+
+import java.lang.reflect.Array;\r
+import java.lang.reflect.Method;\r
+import java.util.HashMap;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.Datatypes;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.RecordBinding;\r
+import org.simantics.databoard.binding.UnionBinding;\r
+import org.simantics.databoard.binding.error.BindingConstructionException;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.error.DatatypeConstructionException;\r
+import org.simantics.databoard.binding.impl.OptionalBindingDefault;\r
+import org.simantics.databoard.type.Component;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.databoard.type.OptionalType;\r
+import org.simantics.databoard.type.RecordType;\r
+import org.simantics.databoard.type.UnionType;\r
+
+public class MethodReflectionBinding {
+
+       private HashMap<Method, MethodTypeDefinition> methodDescriptionCache = 
+               new HashMap<Method, MethodTypeDefinition>();
+       
+       private HashMap<Method, MethodTypeBinding> methodBindingCache = 
+               new HashMap<Method, MethodTypeBinding>();
+       
+       private HashMap<Class<?>, MethodTypeBinding[]> interfaceBindingCache =
+               new HashMap<Class<?>, MethodTypeBinding[]>();
+       
+       private HashMap<Class<?>, Interface> interfaceTypeCache =
+               new HashMap<Class<?>, Interface>();
+
+    /**
+     * Get method binding of a method. 
+     * Method arguments are wrapped into an Object[].
+     * Throwables in an UnionType. 
+     * 
+     * @param m
+     * @return method bindings
+     * @throws BindingConstructionException 
+     */
+    public synchronized MethodTypeBinding getMethodBinding(Method m) 
+    throws BindingConstructionException
+    {
+       MethodTypeBinding mb = methodBindingCache.get(m);
+       if (mb==null) {
+               mb = createMethodBinding(m);
+               methodBindingCache.put(m, mb);
+       }
+       return mb;
+    }
+
+    private MethodTypeBinding createMethodBinding(Method m) 
+    throws BindingConstructionException
+    {
+       try {
+               MethodTypeDefinition md = getMethodDescription(m);
+               Class<?> returnClass = m.getReturnType();
+               Class<?>[] paramClasses = m.getParameterTypes();
+               Class<?>[] errorClasses = m.getExceptionTypes();
+               return createMethodBinding(md, paramClasses, returnClass, errorClasses);
+       } catch (DatatypeConstructionException e) {
+               throw new BindingConstructionException(e);
+       }
+    }
+    
+    private MethodTypeBinding createMethodBinding(MethodTypeDefinition md, Class<?>[] paramClasses, Class<?> returnClass, Class<?>[] errorClasses) 
+    throws BindingConstructionException
+    {
+       RecordBinding requestBinding;
+       Binding responseBinding;
+       UnionBinding errorBinding;
+       
+       responseBinding = Bindings.getBinding(returnClass);
+       
+       // Wrap arguments into Object[]
+//     if (paramClasses.length==0) requestBinding = getBinding(void.class);
+//     else 
+       {
+               ObjectArrayRecordBinding rb = new ObjectArrayRecordBinding();                   
+                       Binding[] cbs = new Binding[paramClasses.length];
+                       rb.setType( md.getType().getRequestType() );
+                       for (int i=0; i<paramClasses.length; i++) {\r
+                               Class<?> paramClass = paramClasses[i];\r
+                               Binding binding = Bindings.getBinding(paramClass);\r
+                               // All arguments are optional\r
+                               binding = new OptionalBindingDefault( binding );
+                               cbs[i] = binding;
+                       }
+                       rb.setComponentBindings(cbs);
+               requestBinding = rb;
+       }
+       
+//     if (errorClasses.length==0) errorBinding = getBinding(void.class);
+//     else 
+       {
+               Datatype type = md.getType().getErrorType();
+               ObjectUnionBinding ub = new ObjectUnionBinding(type, errorClasses);     
+                       Binding cbs[] = new Binding[errorClasses.length];
+               for (int i=0; i<errorClasses.length; i++)
+               {
+                       cbs[i] = Bindings.getBinding(errorClasses[i]);
+               }                               
+                       ub.setComponentBindings(cbs); 
+               errorBinding = ub;
+       }
+       
+       return new MethodTypeBinding(md, requestBinding, responseBinding, errorBinding);
+    }
+
+    /**
+     * Get method description
+     * 
+     * @param m
+     * @return method description
+     * @throws DatatypeConstructionException 
+     */
+    public synchronized MethodTypeDefinition getMethodDescription(Method m) throws DatatypeConstructionException
+    {
+       MethodTypeDefinition md = methodDescriptionCache.get(m);
+       if (md==null) {
+               md = createMethodDescription(m);
+               methodDescriptionCache.put(m, md);
+       }
+       return md;
+    }
+    
+    /**
+     * Get method type
+     * 
+     * @param m
+     * @return method type
+     * @throws DatatypeConstructionException 
+     */
+    public synchronized MethodType getMethodType(Method m) throws DatatypeConstructionException
+    {
+       MethodTypeDefinition md = methodDescriptionCache.get(m);
+       if (md==null) {
+               md = createMethodDescription(m);
+               methodDescriptionCache.put(m, md);
+       }
+       return md.getType();
+    }
+    
+    // :unsynchronized
+    private MethodTypeDefinition createMethodDescription(Method m) throws DatatypeConstructionException
+    {
+       Class<?> returnClass = m.getReturnType();
+       Class<?>[] paramClasses = m.getParameterTypes();
+       Class<?>[] errorClasses = m.getExceptionTypes();
+       
+       RecordType requestType;
+       Datatype responseType;
+       UnionType errorType;
+       
+       responseType = Datatypes.getDatatype(returnClass);
+       
+//     if (paramClasses.length==0) requestType = getDataType(void.class);
+//     else if (paramClasses.length==1) requestType = getDataType(paramClasses[0]);
+//     else 
+       {               
+               RecordType rt = new RecordType();
+               Component[] components = new Component[paramClasses.length];
+               rt.setReferable( false );
+               for (int i=0; i<paramClasses.length; i++) {\r
+                       Datatype paramType = Datatypes.getDatatype( paramClasses[i] );\r
+                       paramType = new OptionalType(paramType);
+                       components[i] = new Component("arg"+(i+1), paramType);\r
+               }
+               rt.setComponents( components );
+               requestType = rt;
+       }
+       
+//     if (errorClasses.length==0) errorType = getDataType(void.class);
+//     else 
+       {
+               UnionType ut = new UnionType();
+               ut.components = new Component[errorClasses.length];
+               for (int i=0; i<errorClasses.length; i++)
+                       ut.components[i] = new Component(
+                                       errorClasses[i].getSimpleName(),
+                                       Datatypes.getDatatype(errorClasses[i]));
+               errorType = ut;
+       }
+       
+       MethodType mt = new MethodType(requestType, responseType, errorType); 
+       MethodTypeDefinition md = new MethodTypeDefinition(m.getName(), mt);
+       return md;
+    }
+    
+
+    public synchronized MethodTypeBinding[] getInterfaceBinding(Class<?> interfaze) 
+    throws BindingConstructionException
+    {
+       MethodTypeBinding[] result = interfaceBindingCache.get( interfaze );
+       if (result==null) {
+               result = createInterfaceBinding(interfaze);
+               interfaceBindingCache.put(interfaze, result);
+       }
+       return result;
+    }
+    
+    private MethodTypeBinding[] createInterfaceBinding(Class<?> interfaze) 
+    throws BindingConstructionException
+    {
+       Method methods[] = interfaze.getMethods();
+       MethodTypeBinding result[] = new MethodTypeBinding[methods.length];
+       for (int i=0; i<methods.length; i++)
+       {
+               result[i] = getMethodBinding( methods[i] );
+       }
+       return result;
+    }
+
+    public synchronized Interface getInterfaceType(Class<?> interfaze) 
+    throws BindingConstructionException
+    {
+       Interface result = interfaceTypeCache.get( interfaze );
+       if (result==null) {
+               result = createInterfaceType(interfaze);
+               interfaceTypeCache.put(interfaze, result);
+       }
+       return result;
+    }
+    
+    private Interface createInterfaceType(Class<?> interfaze) 
+    throws BindingConstructionException
+    {
+       MethodTypeBinding[] bindings = getInterfaceBinding(interfaze);
+       MethodTypeDefinition defs[] = new MethodTypeDefinition[bindings.length];
+       for (int i=0; i<bindings.length; i++)
+       {
+               defs[i] = bindings[i].getMethodDefinition();
+       }
+       return new Interface(defs);
+    }
+
+
+       
+       
+}
+
+/**
+ * Binds RecordType to Object[]
+ */
+class ObjectArrayRecordBinding extends RecordBinding {
+       
+       public ObjectArrayRecordBinding() {             
+       }
+       
+       @Override
+       public void setComponentBindings(Binding[] componentBindings) {
+               super.setComponentBindings(componentBindings);
+       }
+       
+       @Override
+       public Object create(Object... value) throws BindingException {
+               return value.clone();
+       }
+       @Override
+       public Object getComponent(Object obj, int index)
+       throws BindingException {
+               return Array.get(obj, index);
+       }
+       @Override
+       public Object createPartial() throws BindingException {
+               return new Object[componentBindings.length];
+       }
+       @Override
+       public void setComponents(Object obj, Object... value)
+       throws BindingException {
+               for (int i=0; i<Array.getLength(obj); i++)                                              
+                       Array.set(obj, i, value[i]);
+       }
+       @Override
+       public void setComponent(Object obj, int index, Object value)
+                       throws BindingException {
+               Array.set(obj, index, value);           
+       }
+       @Override
+       public boolean isInstance(Object obj) {
+               return obj instanceof Object[];
+       }
+       @Override
+       public void setType(Datatype type) {
+               super.setType(type);
+       }
+}
+
+/**
+ * Binds UnionType to java.lang.Object
+ *
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+class ObjectUnionBinding extends UnionBinding {
+       
+       Class<?>[] classes;
+       
+       public ObjectUnionBinding(Datatype type, Class<?>[] classes) {
+               this.classes = classes;
+               this.type = type;
+       }
+       
+       @Override
+       public Object create(int tag, Object value)
+       throws BindingException {
+               return value;
+       }
+    @Override
+    public void setValue(Object union, int tag, Object value)
+       throws BindingException {
+       throw new BindingException("Cannot change the class of an instance");
+    }
+    @Override
+       public int getTag(Object obj) throws BindingException {
+               for (int i=0; i<classes.length; i++)
+                       if (classes[i].isInstance(obj))
+                               return i;               \r
+               throw new BindingException(obj+" is not an element");\r
+       }
+       @Override
+       public Object getValue(Object obj) throws BindingException {
+               return obj;
+       }
+       @Override
+       public boolean isInstance(Object obj) {
+               for (Class<?> clazz : classes)
+                       if (clazz.isInstance(obj)) 
+                               return true;
+               return false;
+       }       
+       
+       @Override
+       public boolean isImmutable() {
+               return true;
+       }
+       \r
+       @Override\r
+       protected boolean baseEquals( Object obj ) {\r
+           return super.baseEquals( obj ) && classes.equals( ((ObjectUnionBinding)obj).classes );\r
+       }\r
+       \r
+       @Override\r
+       protected int baseHashCode() {\r
+           return super.baseHashCode() + 27 * classes.hashCode();\r
+       }
+}