Mercurial Hosting > luan
diff src/luan/LuanJavaFunction.java @ 775:1a68fc55a80c
simplify dir structure
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Fri, 26 Aug 2016 14:36:40 -0600 |
parents | core/src/luan/LuanJavaFunction.java@cdc70de628b5 |
children | 4083f5a67c63 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/LuanJavaFunction.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,572 @@ +package luan; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.Arrays; +import java.util.Collection; + + +public final class LuanJavaFunction extends LuanFunction { + private final JavaMethod method; + private Object obj; + private final RtnConverter rtnConverter; + private final boolean takesLuaState; + private final ArgConverter[] argConverters; + private final Class varArgCls; + + public LuanJavaFunction(Method method,Object obj) { + this( JavaMethod.of(method), obj ); + } + + public LuanJavaFunction(Constructor constr,Object obj) { + this( JavaMethod.of(constr), obj ); + } + + private LuanJavaFunction(JavaMethod method,Object obj) { + this.method = method; + this.obj = obj; + this.rtnConverter = getRtnConverter(method); + this.takesLuaState = takesLuaState(method); + this.argConverters = getArgConverters(takesLuaState,method); + if( method.isVarArgs() ) { + Class[] paramTypes = method.getParameterTypes(); + this.varArgCls = paramTypes[paramTypes.length-1].getComponentType(); + } else { + this.varArgCls = null; + } + } +/* + private LuanJavaFunction(LuanJavaFunction f) { + this.method = f.method; + this.rtnConverter = f.rtnConverter; + this.takesLuaState = f.takesLuaState; + this.argConverters = f.argConverters; + this.varArgCls = f.varArgCls; + } + + @Override public LuanJavaFunction shallowClone() { + return obj==null ? this : new LuanJavaFunction(this); + } + + @Override public void deepenClone(LuanJavaFunction clone,DeepCloner cloner) { + clone.obj = cloner.get(obj); + } +*/ + @Override public String toString() { + return "java-function: " + method; + } + + public Class[] getParameterTypes() { + return method.getParameterTypes(); + } + + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + try { + args = fixArgs(luan,args); + return doCall(args); + } catch(IllegalArgumentException e) { + checkArgs(args); + throw e; + } + } + + public Object rawCall(LuanState luan,Object[] args) throws LuanException { + args = fixArgs(luan,args); + return doCall(args); + } + + private Object doCall(Object[] args) throws LuanException { + Object rtn; + try { + rtn = method.invoke(obj,args); + } catch(IllegalAccessException e) { + throw new RuntimeException("method = "+method,e); + } catch(InvocationTargetException e) { + Throwable cause = e.getCause(); + if( cause instanceof Error ) + throw (Error)cause; + if( cause instanceof LuanException ) + throw (LuanException)cause; + throw new LuanException(cause); + } catch(InstantiationException e) { + throw new RuntimeException(e); + } + return rtnConverter.convert(rtn); + } + + private static final Map primitiveMap = new HashMap(); + static { + primitiveMap.put(Boolean.TYPE,Boolean.class); + primitiveMap.put(Character.TYPE,Character.class); + primitiveMap.put(Byte.TYPE,Byte.class); + primitiveMap.put(Short.TYPE,Short.class); + primitiveMap.put(Integer.TYPE,Integer.class); + primitiveMap.put(Long.TYPE,Long.class); + primitiveMap.put(Float.TYPE,Float.class); + primitiveMap.put(Double.TYPE,Double.class); + primitiveMap.put(Void.TYPE,Void.class); + } + + private void checkArgs(Object[] args) throws LuanException { + Class[] a = getParameterTypes(); + int start = takesLuaState ? 1 : 0; + for( int i=start; i<a.length; i++ ) { + Class paramType = a[i]; + Class type = paramType; + if( type.isPrimitive() ) + type = (Class)primitiveMap.get(type); + Object arg = args[i]; + if( !type.isInstance(arg) ) { + String expected; + if( i==a.length-1 && method.isVarArgs() ) + expected = fixType(paramType.getComponentType().getSimpleName())+"..."; + else + expected = fixType(paramType.getSimpleName()); + if( arg==null ) { + if( paramType.isPrimitive() ) + throw new LuanException("bad argument #"+(i+1-start)+" ("+expected+" expected, got nil)"); + } else { + String got = fixType(arg.getClass().getSimpleName()); + throw new LuanException("bad argument #"+(i+1-start)+" ("+expected+" expected, got "+got+")"); + } + } + } + } + + private static String fixType(String type) { + if( type.equals("byte[]") ) + return "binary"; + if( type.equals("Double") ) + return "number"; + if( type.equals("LuanTable") ) + return "table"; + if( type.equals("Boolean") ) + return "boolean"; + if( type.equals("String") ) + return "string"; + if( type.equals("Closure") ) + return "function"; + if( type.equals("LuanJavaFunction") ) + return "function"; + return type; + } + + private Object[] fixArgs(LuanState luan,Object[] args) throws LuanException { + int n = argConverters.length; + Object[] rtn; + int start = 0; + if( !takesLuaState && varArgCls==null && args.length == n ) { + rtn = args; + } else { + if( takesLuaState ) + n++; + rtn = new Object[n]; + if( takesLuaState ) { + rtn[start++] = luan; + } + n = argConverters.length; + if( varArgCls != null ) { + n--; + if( args.length < argConverters.length ) { + rtn[rtn.length-1] = Array.newInstance(varArgCls,0); + } else { + int len = args.length - n; + Object varArgs = Array.newInstance(varArgCls,len); + ArgConverter ac = argConverters[n]; + for( int i=0; i<len; i++ ) { + Array.set( varArgs, i, ac.convert(luan,args[n+i]) ); + } + rtn[rtn.length-1] = varArgs; + } + } + System.arraycopy(args,0,rtn,start,Math.min(args.length,n)); + } + for( int i=0; i<n; i++ ) { + rtn[start+i] = argConverters[i].convert(luan,rtn[start+i]); + } + return rtn; + } + + + private interface RtnConverter { + public Object convert(Object obj); + } + + private static final RtnConverter RTN_NOTHING = new RtnConverter() { + @Override public Object[] convert(Object obj) { + return NOTHING; + } + }; + + private static final RtnConverter RTN_SAME = new RtnConverter() { + @Override public Object convert(Object obj) { + return obj; + } + }; + + private static final RtnConverter RTN_ARRAY = new RtnConverter() { + @Override public Object convert(Object obj) { + if( obj == null ) + return null; + Object[] a = new Object[Array.getLength(obj)]; + for( int i=0; i<a.length; i++ ) { + a[i] = Array.get(obj,i); + } + return new LuanTable(new ArrayList<Object>(Arrays.asList(a))); + } + }; + + private static RtnConverter getRtnConverter(JavaMethod m) { + Class rtnType = m.getReturnType(); + if( rtnType == Void.TYPE ) + return RTN_NOTHING; + if( !m.isLuan() && rtnType.isArray() && !rtnType.getComponentType().isPrimitive() ) { +//System.out.println("qqqqqq "+m); + return RTN_ARRAY; + } + return RTN_SAME; + } + + private static boolean isNumber(Class rtnType) { + return rtnType == Short.TYPE + || rtnType == Integer.TYPE + || rtnType == Long.TYPE + || rtnType == Float.TYPE + || rtnType == Double.TYPE + ; + } + + private interface ArgConverter { + public Object convert(LuanState luan,Object obj) throws LuanException; + } + + private static final ArgConverter ARG_SAME = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + return obj; + } + @Override public String toString() { + return "ARG_SAME"; + } + }; + + private static final ArgConverter ARG_DOUBLE = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj instanceof Double ) + return obj; + if( obj instanceof Number ) { + Number n = (Number)obj; + return n.doubleValue(); + } + return obj; + } + @Override public String toString() { + return "ARG_DOUBLE"; + } + }; + + private static final ArgConverter ARG_FLOAT = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj instanceof Float ) + return obj; + if( obj instanceof Number ) { + Number n = (Number)obj; + return n.floatValue(); + } + return obj; + } + @Override public String toString() { + return "ARG_FLOAT"; + } + }; + + private static final ArgConverter ARG_LONG = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj instanceof Long ) + return obj; + if( obj instanceof Number ) { + Number n = (Number)obj; + long r = n.longValue(); + if( r==n.doubleValue() ) + return r; + } + return obj; + } + @Override public String toString() { + return "ARG_LONG"; + } + }; + + private static final ArgConverter ARG_INTEGER = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj instanceof Integer ) + return obj; + if( obj instanceof Number ) { + Number n = (Number)obj; + int r = n.intValue(); + if( r==n.doubleValue() ) + return r; + } + return obj; + } + @Override public String toString() { + return "ARG_INTEGER"; + } + }; + + private static final ArgConverter ARG_SHORT = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj instanceof Short ) + return obj; + if( obj instanceof Number ) { + Number n = (Number)obj; + short r = n.shortValue(); + if( r==n.doubleValue() ) + return r; + } + return obj; + } + @Override public String toString() { + return "ARG_SHORT"; + } + }; + + private static final ArgConverter ARG_BYTE = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj instanceof Byte ) + return obj; + if( obj instanceof Number ) { + Number n = (Number)obj; + byte r = n.byteValue(); + if( r==n.doubleValue() ) + return r; + } + return obj; + } + @Override public String toString() { + return "ARG_BYTE"; + } + }; + + private static final ArgConverter ARG_TABLE = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj == null ) + return null; + if( obj instanceof List ) { + return new LuanTable((List)obj); + } + if( obj instanceof Map ) { + return new LuanTable((Map)obj); + } + if( obj instanceof Set ) { + return new LuanTable((Set)obj); + } + Class cls = obj.getClass(); + if( cls.isArray() && !cls.getComponentType().isPrimitive() ) { + Object[] a = (Object[])obj; + return new LuanTable(Arrays.asList(a)); + } + return obj; + } + @Override public String toString() { + return "ARG_TABLE"; + } + }; + + private static final ArgConverter ARG_MAP = new ArgConverter() { + public Object convert(LuanState luan,Object obj) throws LuanException { + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + return t.asMap(luan); + } + return obj; + } + @Override public String toString() { + return "ARG_MAP"; + } + }; + + private static final ArgConverter ARG_LIST = new ArgConverter() { + public Object convert(LuanState luan,Object obj) { + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + if( t.isList() ) + return t.asList(); + } + return obj; + } + @Override public String toString() { + return "ARG_LIST"; + } + }; + + private static final ArgConverter ARG_SET = new ArgConverter() { + public Object convert(LuanState luan,Object obj) throws LuanException { + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + if( t.isSet(luan) ) + return t.asSet(luan); + } + return obj; + } + @Override public String toString() { + return "ARG_SET"; + } + }; + + private static final ArgConverter ARG_COLLECTION = new ArgConverter() { + public Object convert(LuanState luan,Object obj) throws LuanException { + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + if( t.isList() ) + return t.asList(); + if( t.isSet(luan) ) + return t.asSet(luan); + } + return obj; + } + @Override public String toString() { + return "ARG_COLLECTION"; + } + }; + + private static class ArgArray implements ArgConverter { + private final Object[] a; + + ArgArray(Class cls) { + a = (Object[])Array.newInstance(cls.getComponentType(),0); + } + + public Object convert(LuanState luan,Object obj) { + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + if( t.isList() ) { + try { + return t.asList().toArray(a); + } catch(ArrayStoreException e) {} + } + } + return obj; + } + } + + private static boolean takesLuaState(JavaMethod m) { + Class[] paramTypes = m.getParameterTypes(); + return paramTypes.length > 0 && paramTypes[0].equals(LuanState.class); + } + + private static ArgConverter[] getArgConverters(boolean takesLuaState,JavaMethod m) { + final boolean isVarArgs = m.isVarArgs(); + Class[] paramTypes = m.getParameterTypes(); + if( takesLuaState ) { + Class[] t = new Class[paramTypes.length-1]; + System.arraycopy(paramTypes,1,t,0,t.length); + paramTypes = t; + } + ArgConverter[] a = new ArgConverter[paramTypes.length]; + for( int i=0; i<a.length; i++ ) { + Class paramType = paramTypes[i]; + if( isVarArgs && i == a.length-1 ) + paramType = paramType.getComponentType(); + a[i] = getArgConverter(paramType); + } + return a; + } + + private static ArgConverter getArgConverter(Class cls) { + if( cls == Double.TYPE || cls.equals(Double.class) ) + return ARG_DOUBLE; + if( cls == Float.TYPE || cls.equals(Float.class) ) + return ARG_FLOAT; + if( cls == Long.TYPE || cls.equals(Long.class) ) + return ARG_LONG; + if( cls == Integer.TYPE || cls.equals(Integer.class) ) + return ARG_INTEGER; + if( cls == Short.TYPE || cls.equals(Short.class) ) + return ARG_SHORT; + if( cls == Byte.TYPE || cls.equals(Byte.class) ) + return ARG_BYTE; + if( cls.equals(LuanTable.class) ) + return ARG_TABLE; + if( cls.equals(Map.class) ) + return ARG_MAP; + if( cls.equals(List.class) ) + return ARG_LIST; + if( cls.equals(Set.class) ) + return ARG_SET; + if( cls.equals(Collection.class) ) + return ARG_COLLECTION; + if( cls.isArray() && !cls.getComponentType().isPrimitive() ) + return new ArgArray(cls); + return ARG_SAME; + } + + + + private static abstract class JavaMethod { + abstract boolean isVarArgs(); + abstract Class[] getParameterTypes(); + abstract Object invoke(Object obj,Object... args) + throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException; + abstract Class getReturnType(); + abstract boolean isLuan(); + + static JavaMethod of(final Method m) { + return new JavaMethod() { + @Override boolean isVarArgs() { + return m.isVarArgs(); + } + @Override Class[] getParameterTypes() { + return m.getParameterTypes(); + } + @Override Object invoke(Object obj,Object... args) + throws IllegalAccessException, IllegalArgumentException, InvocationTargetException + { + return m.invoke(obj,args); + } + @Override Class getReturnType() { + return m.getReturnType(); + } + @Override boolean isLuan() { + return m.getAnnotation(LuanMethod.class) != null; + } + @Override public String toString() { + return m.toString(); + } + }; + } + + static JavaMethod of(final Constructor c) { + return new JavaMethod() { + @Override boolean isVarArgs() { + return c.isVarArgs(); + } + @Override Class[] getParameterTypes() { + return c.getParameterTypes(); + } + @Override Object invoke(Object obj,Object... args) + throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException + { + return c.newInstance(args); + } + @Override Class getReturnType() { + return c.getDeclaringClass(); + } + @Override boolean isLuan() { + return false; + } + @Override public String toString() { + return c.toString(); + } + }; + } + + } + +}