Mercurial Hosting > luan
diff src/luan/lib/JavaLib.java @ 37:8a57ebfdfd78
add JavaLib
git-svn-id: https://luan-java.googlecode.com/svn/trunk@38 21e917c8-12df-6dd8-5cb6-c86387c605b9
author | fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9> |
---|---|
date | Thu, 20 Dec 2012 02:36:07 +0000 |
parents | |
children | e3624b7cd603 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/lib/JavaLib.java Thu Dec 20 02:36:07 2012 +0000 @@ -0,0 +1,300 @@ +package luan.lib; + +import java.lang.reflect.Array; +import java.lang.reflect.Member; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import luan.LuaNumber; +import luan.LuaState; +import luan.LuaTable; +import luan.MetatableGetter; +import luan.LuaException; +import luan.LuaFunction; +import luan.LuaJavaFunction; + + +public final class JavaLib { + + public static void register(LuaState lua) { + lua.addMetatableGetter(mg); + LuaTable module = new LuaTable(); + LuaTable global = lua.global(); + global.put("java",module); + try { + global.put( "import", new LuaJavaFunction(JavaLib.class.getMethod("importClass",LuaState.class,String.class),null) ); + module.put( "class", new LuaJavaFunction(JavaLib.class.getMethod("getClass",String.class),null) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + add( global, "ipairs", Object.class ); + add( global, "pairs", Object.class ); + } + + private static final LuaTable mt = new LuaTable(); + static { + add( mt, "__index", Object.class, Object.class ); + } + + private static void add(LuaTable t,String method,Class<?>... parameterTypes) { + try { + t.put( method, new LuaJavaFunction(JavaLib.class.getMethod(method,parameterTypes),null) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + private static final MetatableGetter mg = new MetatableGetter() { + public LuaTable getMetatable(Object obj) { + if( obj==null ) + return null; + return mt; + } + }; + + public static Object __index(Object obj,Object key) throws LuaException { + if( obj instanceof Static ) { + if( key instanceof String ) { + String name = (String)key; + Static st = (Static)obj; + Class cls = st.cls; + if( "class".equals(name) ) { + return cls; + } else if( "new".equals(name) ) { + Constructor<?>[] constructors = cls.getConstructors(); + if( constructors.length > 0 ) { + if( constructors.length==1 ) { + return new LuaJavaFunction(constructors[0],null); + } else { + List<LuaJavaFunction> fns = new ArrayList<LuaJavaFunction>(); + for( Constructor constructor : constructors ) { + fns.add(new LuaJavaFunction(constructor,null)); + } + return new AmbiguousJavaFunction(fns); + } + } + } else { + List<Member> members = getStaticMembers(cls,name); + if( !members.isEmpty() ) { + return member(null,members); + } + } + } + throw new LuaException("invalid index for java class: "+key); + } + Class cls = obj.getClass(); + if( cls.isArray() ) { + if( "length".equals(key) ) { + return Array.getLength(obj); + } + if( key instanceof LuaNumber ) { + LuaNumber n = (LuaNumber)key; + double d = n.value(); + int i = (int)d; + if( d==i ) { + return Array.get(obj,i); + } + } + throw new LuaException("invalid index for java array: "+key); + } + if( key instanceof String ) { + String name = (String)key; + if( "instanceof".equals(name) ) { + return new LuaJavaFunction(instanceOf,new InstanceOf(obj)); + } else { + List<Member> members = getMembers(cls,name); + if( !members.isEmpty() ) { + return member(obj,members); + } + } + } + throw new LuaException("invalid index for java object: "+key); + } + + private static Object member(Object obj,List<Member> members) throws LuaException { + try { + if( members.size()==1 ) { + Member member = members.get(0); + if( member instanceof Field ) { + Field field = (Field)member; + Object value = field.get(obj); + if( value instanceof Number ) { + Number n = (Number)value; + value = new LuaNumber(n.doubleValue()); + } + return value; + } else { + Method method = (Method)member; + return new LuaJavaFunction(method,obj); + } + } else { + List<LuaJavaFunction> fns = new ArrayList<LuaJavaFunction>(); + for( Member member : members ) { + Method method = (Method)member; + fns.add(new LuaJavaFunction(method,obj)); + } + return new AmbiguousJavaFunction(fns); + } + } catch(IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static Map<Class,Map<String,List<Member>>> memberMap = new HashMap<Class,Map<String,List<Member>>>(); + + private static synchronized List<Member> getMembers(Class cls,String name) { + Map<String,List<Member>> clsMap = memberMap.get(cls); + if( clsMap == null ) { + clsMap = new HashMap<String,List<Member>>(); + for( Field field : cls.getFields() ) { + String s = field.getName(); + List<Member> list = clsMap.get(s); + if( list == null ) { + list = new ArrayList<Member>(); + clsMap.put(s,list); + } + list.add(field); + } + for( Method method : cls.getMethods() ) { + String s = method.getName(); + List<Member> list = clsMap.get(s); + if( list == null ) { + list = new ArrayList<Member>(); + clsMap.put(s,list); + } + list.add(method); + } + memberMap.put(cls,clsMap); + } + return clsMap.get(name); + } + + private static synchronized List<Member> getStaticMembers(Class cls,String name) { + List<Member> staticMembers = new ArrayList<Member>(); + for( Member m : getMembers(cls,name) ) { + if( Modifier.isStatic(m.getModifiers()) ) + staticMembers.add(m); + } + return staticMembers; + } + + static class Static { + final Class cls; + + Static(Class cls) { + this.cls = cls; + } + } + + public static Static getClass(String name) throws LuaException { + try { + return new Static( Class.forName(name) ); + } catch(ClassNotFoundException e) { + throw new LuaException(e); + } + } + + public static void importClass(LuaState lua,String name) throws LuaException { + lua.global().put( name.substring(name.lastIndexOf('.')+1), getClass(name) ); + } + + static class AmbiguousJavaFunction extends LuaFunction { + private final Map<Integer,List<LuaJavaFunction>> fnMap = new HashMap<Integer,List<LuaJavaFunction>>(); + + AmbiguousJavaFunction(List<LuaJavaFunction> fns) { + for( LuaJavaFunction fn : fns ) { + Integer n = fn.getParameterTypes().length; + List<LuaJavaFunction> list = fnMap.get(n); + if( list==null ) { + list = new ArrayList<LuaJavaFunction>(); + fnMap.put(n,list); + } + list.add(fn); + } + } + + @Override public Object[] call(LuaState lua,Object... args) throws LuaException { + for( LuaJavaFunction fn : fnMap.get(args.length) ) { + try { + return fn.call(lua,args); + } catch(IllegalArgumentException e) {} + } + throw new LuaException("no method matched args"); + } + } + + + public static LuaFunction pairs(Object t) throws LuaException { + if( t instanceof LuaTable ) + return BasicLib.pairs((LuaTable)t); + if( t instanceof Map ) { + @SuppressWarnings("unchecked") + Map<Object,Object> m = (Map<Object,Object>)t; + return BasicLib.pairs(m.entrySet().iterator()); + } + throw new LuaException( "bad argument #1 to 'pairs' (table or Map expected)" ); + } + + private static class Iter { + private final Iterator iter; + private double i = 0.0; + + Iter(Iterable t) { + this.iter = t.iterator(); + } + + public Object[] next() { + if( !iter.hasNext() ) + return LuaFunction.EMPTY_RTN ; + return new Object[]{ new LuaNumber(i++), iter.next() }; + } + } + private static final Method nextIter; + static { + try { + nextIter = Iter.class.getMethod("next"); + nextIter.setAccessible(true); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public static LuaFunction ipairs(Object t) throws LuaException { + if( t instanceof LuaTable ) + return BasicLib.ipairs((LuaTable)t); + if( t instanceof Iterable ) { + Iter ai = new Iter((Iterable)t); + return new LuaJavaFunction(nextIter,ai); + } + throw new LuaException( "bad argument #1 to 'ipairs' (table or Iterable expected)" ); + } + + + private static class InstanceOf { + private final Object obj; + + InstanceOf(Object obj) { + this.obj = obj; + } + + public boolean instanceOf(Static st) { + return st.cls.isInstance(obj); + } + } + private static final Method instanceOf; + static { + try { + instanceOf = InstanceOf.class.getMethod("instanceOf",Static.class); + instanceOf.setAccessible(true); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + +}