view src/luan/lib/JavaLib.java @ 51:28dfd91a816c

various small issues git-svn-id: https://luan-java.googlecode.com/svn/trunk@52 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Fri, 28 Dec 2012 20:43:06 +0000
parents 272b6f1d7dac
children 9381b23ea9e1
line wrap: on
line source

package luan.lib;

import java.lang.reflect.Array;
import java.lang.reflect.AccessibleObject;
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.Luan;
import luan.LuanState;
import luan.LuanTable;
import luan.MetatableGetter;
import luan.LuanException;
import luan.LuanFunction;
import luan.LuanJavaFunction;
import luan.LuanElement;


public final class JavaLib {

	public static void register(LuanState luan) {
		luan.addMetatableGetter(mg);
		LuanTable module = new LuanTable();
		LuanTable global = luan.global();
		global.put("java",module);
		try {
			global.put( "import", new LuanJavaFunction(JavaLib.class.getMethod("importClass",LuanState.class,String.class),null) );
			module.put( "class", new LuanJavaFunction(JavaLib.class.getMethod("getClass",LuanState.class,String.class),null) );
		} catch(NoSuchMethodException e) {
			throw new RuntimeException(e);
		}
	}

	private static final LuanTable mt = new LuanTable();
	static {
		add( mt, "__index", LuanState.class, Object.class, Object.class );
	}

	private static void add(LuanTable t,String method,Class<?>... parameterTypes) {
		try {
			t.put( method, new LuanJavaFunction(JavaLib.class.getMethod(method,parameterTypes),null) );
		} catch(NoSuchMethodException e) {
			throw new RuntimeException(e);
		}
	}

	private static final MetatableGetter mg = new MetatableGetter() {
		public LuanTable getMetatable(Object obj) {
			if( obj==null )
				return null;
			return mt;
		}
	};

	public static Object __index(LuanState luan,Object obj,Object key) throws LuanException {
		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 LuanJavaFunction(constructors[0],null);
						} else {
							List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>();
							for( Constructor constructor : constructors ) {
								fns.add(new LuanJavaFunction(constructor,null));
							}
							return new AmbiguousJavaFunction(fns);
						}
					}
				} else {
					List<AccessibleObject> members = getStaticMembers(cls,name);
					if( !members.isEmpty() ) {
						return member(null,members);
					}
				}
			}
			throw new LuanException(luan,LuanElement.JAVA,"invalid index for java class: "+key);
		}
		Class cls = obj.getClass();
		if( cls.isArray() ) {
			if( "length".equals(key) ) {
				return Array.getLength(obj);
			}
			Integer i = Luan.asInteger(key);
			if( i != null ) {
				return Array.get(obj,i);
			}
			throw new LuanException(luan,LuanElement.JAVA,"invalid index for java array: "+key);
		}
		if( key instanceof String ) {
			String name = (String)key;
			if( "instanceof".equals(name) ) {
				return new LuanJavaFunction(instanceOf,new InstanceOf(obj));
			} else {
				List<AccessibleObject> members = getMembers(cls,name);
				if( !members.isEmpty() ) {
					return member(obj,members);
				}
			}
		}
		throw new LuanException(luan,LuanElement.JAVA,"invalid index for java object: "+key);
	}

	private static Object member(Object obj,List<AccessibleObject> members) throws LuanException {
		try {
			for( AccessibleObject m : members ) {
				m.setAccessible(true);
			}
			if( members.size()==1 ) {
				AccessibleObject member = members.get(0);
				if( member instanceof Field ) {
					Field field = (Field)member;
					return field.get(obj);
				} else {
					Method method = (Method)member;
					return new LuanJavaFunction(method,obj);
				}
			} else {
				List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>();
				for( AccessibleObject member : members ) {
					Method method = (Method)member;
					fns.add(new LuanJavaFunction(method,obj));
				}
				return new AmbiguousJavaFunction(fns);
			}
		} catch(IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}

	private static Map<Class,Map<String,List<AccessibleObject>>> memberMap = new HashMap<Class,Map<String,List<AccessibleObject>>>();

	private static synchronized List<AccessibleObject> getMembers(Class cls,String name) {
		Map<String,List<AccessibleObject>> clsMap = memberMap.get(cls);
		if( clsMap == null ) {
			clsMap = new HashMap<String,List<AccessibleObject>>();
			for( Field field : cls.getFields() ) {
				String s = field.getName();
				List<AccessibleObject> list = clsMap.get(s);
				if( list == null ) {
					list = new ArrayList<AccessibleObject>();
					clsMap.put(s,list);
				}
				list.add(field);
			}
			for( Method method : cls.getMethods() ) {
				String s = method.getName();
				List<AccessibleObject> list = clsMap.get(s);
				if( list == null ) {
					list = new ArrayList<AccessibleObject>();
					clsMap.put(s,list);
				}
				list.add(method);
			}
			memberMap.put(cls,clsMap);
		}
		return clsMap.get(name);
	}

	private static synchronized List<AccessibleObject> getStaticMembers(Class cls,String name) {
		List<AccessibleObject> staticMembers = new ArrayList<AccessibleObject>();
		for( AccessibleObject m : getMembers(cls,name) ) {
			if( Modifier.isStatic(((Member)m).getModifiers()) )
				staticMembers.add(m);
		}
		return staticMembers;
	}

	static class Static {
		final Class cls;

		Static(Class cls) {
			this.cls = cls;
		}
	}

	public static Static getClass(LuanState luan,String name) throws LuanException {
		Class cls;
		try {
			cls = Class.forName(name);
		} catch(ClassNotFoundException e) {
			try {
				cls = Thread.currentThread().getContextClassLoader().loadClass(name);
			} catch(ClassNotFoundException e2) {
				throw new LuanException(luan,LuanElement.JAVA,e);
			}
		}
		return new Static(cls);
	}

	public static void importClass(LuanState luan,String name) throws LuanException {
		luan.global().put( name.substring(name.lastIndexOf('.')+1), getClass(luan,name) );
	}

	static class AmbiguousJavaFunction extends LuanFunction {
		private final Map<Integer,List<LuanJavaFunction>> fnMap = new HashMap<Integer,List<LuanJavaFunction>>();

		AmbiguousJavaFunction(List<LuanJavaFunction> fns) {
			for( LuanJavaFunction fn : fns ) {
				Integer n = fn.getParameterTypes().length;
				List<LuanJavaFunction> list = fnMap.get(n);
				if( list==null ) {
					list = new ArrayList<LuanJavaFunction>();
					fnMap.put(n,list);
				}
				list.add(fn);
			}
		}

		@Override public Object[] call(LuanState luan,Object[] args) throws LuanException {
			for( LuanJavaFunction fn : fnMap.get(args.length) ) {
				try {
					return fn.call(luan,args);
				} catch(IllegalArgumentException e) {}
			}
			throw new LuanException(luan,LuanElement.JAVA,"no method matched args");
		}
	}

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

}