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);
+		}
+	}
+
+}