|  | /* | 
|  | * $Id: DefaultConverter.java 161 2012-10-06 13:53:02Z andre@naef.com $ | 
|  | * See LICENSE.txt for license terms. | 
|  | */ | 
|  |  | 
|  | package com.naef.jnlua; | 
|  |  | 
|  | import java.lang.reflect.Array; | 
|  | import java.math.BigDecimal; | 
|  | import java.math.BigInteger; | 
|  | import java.util.HashMap; | 
|  | import java.util.List; | 
|  | import java.util.Map; | 
|  |  | 
|  | import com.naef.jnlua.util.AbstractTableList; | 
|  | import com.naef.jnlua.util.AbstractTableMap; | 
|  |  | 
|  | /** | 
|  | * Default implementation of the <code>Converter</code> interface. | 
|  | */ | 
|  | public class DefaultConverter implements Converter { | 
|  | // -- Static | 
|  | /** | 
|  | * Raw byte array. | 
|  | */ | 
|  | private static final boolean RAW_BYTE_ARRAY = Boolean.parseBoolean(System | 
|  | .getProperty(DefaultConverter.class.getPackage().getName() | 
|  | + ".rawByteArray")); | 
|  |  | 
|  | /** | 
|  | * Static instance. | 
|  | */ | 
|  | private static final DefaultConverter INSTANCE = new DefaultConverter(); | 
|  |  | 
|  | public static boolean isTypeSupported(Class<?> clazz) { | 
|  | return JAVA_OBJECT_CONVERTERS.get(clazz) != null; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Boolean distance map. | 
|  | */ | 
|  | private static final Map<Class<?>, Integer> BOOLEAN_DISTANCE_MAP = new HashMap<Class<?>, Integer>(); | 
|  | static { | 
|  | BOOLEAN_DISTANCE_MAP.put(Boolean.class, new Integer(1)); | 
|  | BOOLEAN_DISTANCE_MAP.put(Boolean.TYPE, new Integer(1)); | 
|  | BOOLEAN_DISTANCE_MAP.put(Object.class, new Integer(2)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Number distance map. | 
|  | */ | 
|  | private static final Map<Class<?>, Integer> NUMBER_DISTANCE_MAP = new HashMap<Class<?>, Integer>(); | 
|  | static { | 
|  | NUMBER_DISTANCE_MAP.put(Byte.class, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(Byte.TYPE, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(Short.class, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(Short.TYPE, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(Integer.class, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(Integer.TYPE, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(Long.class, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(Long.TYPE, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(Float.class, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(Float.TYPE, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(Double.class, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(Double.TYPE, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(BigInteger.class, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(BigDecimal.class, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(Character.class, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(Character.TYPE, new Integer(1)); | 
|  | NUMBER_DISTANCE_MAP.put(Object.class, new Integer(2)); | 
|  | NUMBER_DISTANCE_MAP.put(String.class, new Integer(3)); | 
|  | if (!RAW_BYTE_ARRAY) { | 
|  | NUMBER_DISTANCE_MAP.put(byte[].class, new Integer(3)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * String distance map. | 
|  | */ | 
|  | private static final Map<Class<?>, Integer> STRING_DISTANCE_MAP = new HashMap<Class<?>, Integer>(); | 
|  | static { | 
|  | STRING_DISTANCE_MAP.put(String.class, new Integer(1)); | 
|  | if (!RAW_BYTE_ARRAY) { | 
|  | STRING_DISTANCE_MAP.put(byte[].class, new Integer(1)); | 
|  | } | 
|  | STRING_DISTANCE_MAP.put(Object.class, new Integer(2)); | 
|  | STRING_DISTANCE_MAP.put(Byte.class, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(Byte.TYPE, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(Short.class, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(Short.TYPE, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(Integer.class, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(Integer.TYPE, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(Long.class, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(Long.TYPE, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(Float.class, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(Float.TYPE, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(Double.class, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(Double.TYPE, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(BigInteger.class, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(BigDecimal.class, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(Character.class, new Integer(3)); | 
|  | STRING_DISTANCE_MAP.put(Character.TYPE, new Integer(3)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Function distance map. | 
|  | */ | 
|  | private static final Map<Class<?>, Integer> FUNCTION_DISTANCE_MAP = new HashMap<Class<?>, Integer>(); | 
|  | static { | 
|  | FUNCTION_DISTANCE_MAP.put(JavaFunction.class, new Integer(1)); | 
|  | FUNCTION_DISTANCE_MAP.put(Object.class, new Integer(2)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Lua value converters. | 
|  | */ | 
|  | private static final Map<Class<?>, LuaValueConverter<?>> LUA_VALUE_CONVERTERS = new HashMap<Class<?>, LuaValueConverter<?>>(); | 
|  | static { | 
|  | LuaValueConverter<Boolean> booleanConverter = new LuaValueConverter<Boolean>() { | 
|  | @Override | 
|  | public Boolean convert(LuaState luaState, int index) { | 
|  | return Boolean.valueOf(luaState.toBoolean(index)); | 
|  | } | 
|  | }; | 
|  | LUA_VALUE_CONVERTERS.put(Boolean.class, booleanConverter); | 
|  | LUA_VALUE_CONVERTERS.put(Boolean.TYPE, booleanConverter); | 
|  |  | 
|  | LuaValueConverter<Byte> byteConverter = new LuaValueConverter<Byte>() { | 
|  | @Override | 
|  | public Byte convert(LuaState luaState, int index) { | 
|  | return Byte.valueOf((byte) luaState.toInteger(index)); | 
|  | } | 
|  | }; | 
|  | LUA_VALUE_CONVERTERS.put(Byte.class, byteConverter); | 
|  | LUA_VALUE_CONVERTERS.put(Byte.TYPE, byteConverter); | 
|  | LuaValueConverter<Short> shortConverter = new LuaValueConverter<Short>() { | 
|  | @Override | 
|  | public Short convert(LuaState luaState, int index) { | 
|  | return Short.valueOf((short) luaState.toInteger(index)); | 
|  | } | 
|  | }; | 
|  | LUA_VALUE_CONVERTERS.put(Short.class, shortConverter); | 
|  | LUA_VALUE_CONVERTERS.put(Short.TYPE, shortConverter); | 
|  | LuaValueConverter<Integer> integerConverter = new LuaValueConverter<Integer>() { | 
|  | @Override | 
|  | public Integer convert(LuaState luaState, int index) { | 
|  | return Integer.valueOf(luaState.toInteger(index)); | 
|  | } | 
|  | }; | 
|  | LUA_VALUE_CONVERTERS.put(Integer.class, integerConverter); | 
|  | LUA_VALUE_CONVERTERS.put(Integer.TYPE, integerConverter); | 
|  | LuaValueConverter<Long> longConverter = new LuaValueConverter<Long>() { | 
|  | @Override | 
|  | public Long convert(LuaState luaState, int index) { | 
|  | return Long.valueOf((long) luaState.toNumber(index)); | 
|  | } | 
|  | }; | 
|  | LUA_VALUE_CONVERTERS.put(Long.class, longConverter); | 
|  | LUA_VALUE_CONVERTERS.put(Long.TYPE, longConverter); | 
|  | LuaValueConverter<Float> floatConverter = new LuaValueConverter<Float>() { | 
|  | @Override | 
|  | public Float convert(LuaState luaState, int index) { | 
|  | return Float.valueOf((float) luaState.toNumber(index)); | 
|  | } | 
|  | }; | 
|  | LUA_VALUE_CONVERTERS.put(Float.class, floatConverter); | 
|  | LUA_VALUE_CONVERTERS.put(Float.TYPE, floatConverter); | 
|  | LuaValueConverter<Double> doubleConverter = new LuaValueConverter<Double>() { | 
|  | @Override | 
|  | public Double convert(LuaState luaState, int index) { | 
|  | return Double.valueOf(luaState.toNumber(index)); | 
|  | } | 
|  | }; | 
|  | LUA_VALUE_CONVERTERS.put(Double.class, doubleConverter); | 
|  | LUA_VALUE_CONVERTERS.put(Double.TYPE, doubleConverter); | 
|  | LuaValueConverter<BigInteger> bigIntegerConverter = new LuaValueConverter<BigInteger>() { | 
|  | @Override | 
|  | public BigInteger convert(LuaState luaState, int index) { | 
|  | return BigDecimal.valueOf(luaState.toNumber(index)) | 
|  | .setScale(0, BigDecimal.ROUND_HALF_EVEN).toBigInteger(); | 
|  | } | 
|  | }; | 
|  | LUA_VALUE_CONVERTERS.put(BigInteger.class, bigIntegerConverter); | 
|  | LuaValueConverter<BigDecimal> bigDecimalConverter = new LuaValueConverter<BigDecimal>() { | 
|  | @Override | 
|  | public BigDecimal convert(LuaState luaState, int index) { | 
|  | return BigDecimal.valueOf(luaState.toNumber(index)); | 
|  | } | 
|  | }; | 
|  | LUA_VALUE_CONVERTERS.put(BigDecimal.class, bigDecimalConverter); | 
|  | LuaValueConverter<Character> characterConverter = new LuaValueConverter<Character>() { | 
|  | @Override | 
|  | public Character convert(LuaState luaState, int index) { | 
|  | return Character.valueOf((char) luaState.toInteger(index)); | 
|  | } | 
|  | }; | 
|  | LUA_VALUE_CONVERTERS.put(Character.class, characterConverter); | 
|  | LUA_VALUE_CONVERTERS.put(Character.TYPE, characterConverter); | 
|  | LuaValueConverter<String> stringConverter = new LuaValueConverter<String>() { | 
|  | @Override | 
|  | public String convert(LuaState luaState, int index) { | 
|  | return luaState.toString(index); | 
|  | } | 
|  | }; | 
|  | LUA_VALUE_CONVERTERS.put(String.class, stringConverter); | 
|  | if (!RAW_BYTE_ARRAY) { | 
|  | LuaValueConverter<byte[]> byteArrayConverter = new LuaValueConverter<byte[]>() { | 
|  | @Override | 
|  | public byte[] convert(LuaState luaState, int index) { | 
|  | return luaState.toByteArray(index); | 
|  | } | 
|  | }; | 
|  | LUA_VALUE_CONVERTERS.put(byte[].class, byteArrayConverter); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Java object converters. | 
|  | */ | 
|  | private static final Map<Class<?>, JavaObjectConverter<?>> JAVA_OBJECT_CONVERTERS = new HashMap<Class<?>, JavaObjectConverter<?>>(); | 
|  | static { | 
|  | JavaObjectConverter<Boolean> booleanConverter = new JavaObjectConverter<Boolean>() { | 
|  | @Override | 
|  | public void convert(LuaState luaState, Boolean booleanValue) { | 
|  | luaState.pushBoolean(booleanValue.booleanValue()); | 
|  | } | 
|  | }; | 
|  | JAVA_OBJECT_CONVERTERS.put(Boolean.class, booleanConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(Boolean.TYPE, booleanConverter); | 
|  | JavaObjectConverter<Number> numberConverter = new JavaObjectConverter<Number>() { | 
|  | @Override | 
|  | public void convert(LuaState luaState, Number number) { | 
|  | luaState.pushNumber(number.doubleValue()); | 
|  | } | 
|  | }; | 
|  | JAVA_OBJECT_CONVERTERS.put(Byte.class, numberConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(Byte.TYPE, numberConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(Short.class, numberConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(Short.TYPE, numberConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(Integer.class, numberConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(Integer.TYPE, numberConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(Long.class, numberConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(Long.TYPE, numberConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(Float.class, numberConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(Float.TYPE, numberConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(Double.class, numberConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(Double.TYPE, numberConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(BigInteger.class, numberConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(BigDecimal.class, numberConverter); | 
|  | JavaObjectConverter<Character> characterConverter = new JavaObjectConverter<Character>() { | 
|  | @Override | 
|  | public void convert(LuaState luaState, Character character) { | 
|  | luaState.pushInteger(character.charValue()); | 
|  | } | 
|  | }; | 
|  | JAVA_OBJECT_CONVERTERS.put(Character.class, characterConverter); | 
|  | JAVA_OBJECT_CONVERTERS.put(Character.TYPE, characterConverter); | 
|  | JavaObjectConverter<String> stringConverter = new JavaObjectConverter<String>() { | 
|  | @Override | 
|  | public void convert(LuaState luaState, String string) { | 
|  | luaState.pushString(string); | 
|  | } | 
|  | }; | 
|  | JAVA_OBJECT_CONVERTERS.put(String.class, stringConverter); | 
|  | if (!RAW_BYTE_ARRAY) { | 
|  | JavaObjectConverter<byte[]> byteArrayConverter = new JavaObjectConverter<byte[]>() { | 
|  | @Override | 
|  | public void convert(LuaState luaState, byte[] byteArray) { | 
|  | luaState.pushByteArray(byteArray); | 
|  | } | 
|  | }; | 
|  | JAVA_OBJECT_CONVERTERS.put(byte[].class, byteArrayConverter); | 
|  | } | 
|  | } | 
|  |  | 
|  | // -- Static methods | 
|  | /** | 
|  | * Returns the instance of this class. | 
|  | * | 
|  | * @return the instance | 
|  | */ | 
|  | public static DefaultConverter getInstance() { | 
|  | return INSTANCE; | 
|  | } | 
|  |  | 
|  | // -- Construction | 
|  | /** | 
|  | * Singleton. | 
|  | */ | 
|  | private DefaultConverter() { | 
|  | } | 
|  |  | 
|  | // -- Java converter methods | 
|  | @Override | 
|  | public int getTypeDistance(LuaState luaState, int index, Class<?> formalType) { | 
|  | // Handle none | 
|  | LuaType luaType = luaState.type(index); | 
|  | if (luaType == null) { | 
|  | return Integer.MAX_VALUE; | 
|  | } | 
|  |  | 
|  | // Handle void | 
|  | if (formalType == Void.TYPE) { | 
|  | return Integer.MAX_VALUE; | 
|  | } | 
|  |  | 
|  | // Handle Lua value proxy | 
|  | if (formalType == LuaValueProxy.class) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // Handle Lua types | 
|  | switch (luaType) { | 
|  | case NIL: | 
|  | return 1; | 
|  | case BOOLEAN: | 
|  | Integer distance = BOOLEAN_DISTANCE_MAP.get(formalType); | 
|  | if (distance != null) { | 
|  | return distance.intValue(); | 
|  | } | 
|  | break; | 
|  | case NUMBER: | 
|  | distance = NUMBER_DISTANCE_MAP.get(formalType); | 
|  | if (distance != null) { | 
|  | return distance.intValue(); | 
|  | } | 
|  | break; | 
|  | case STRING: | 
|  | distance = STRING_DISTANCE_MAP.get(formalType); | 
|  | if (distance != null) { | 
|  | return distance.intValue(); | 
|  | } | 
|  | break; | 
|  | case TABLE: | 
|  | if (formalType == Map.class || formalType == List.class | 
|  | || formalType.isArray()) { | 
|  | return 1; | 
|  | } | 
|  | if (formalType == Object.class) { | 
|  | return 2; | 
|  | } | 
|  | break; | 
|  | case FUNCTION: | 
|  | if (luaState.isJavaFunction(index)) { | 
|  | distance = FUNCTION_DISTANCE_MAP.get(formalType); | 
|  | if (distance != null) { | 
|  | return distance.intValue(); | 
|  | } | 
|  | } | 
|  | break; | 
|  | case USERDATA: | 
|  | Object object = luaState.toJavaObjectRaw(index); | 
|  | if (object != null) { | 
|  | Class<?> type; | 
|  | if (object instanceof TypedJavaObject) { | 
|  | TypedJavaObject typedJavaObject = (TypedJavaObject) object; | 
|  | if (typedJavaObject.isStrong()) { | 
|  | if (formalType.isAssignableFrom(typedJavaObject | 
|  | .getClass())) { | 
|  | return 1; | 
|  | } | 
|  | } | 
|  | type = typedJavaObject.getType(); | 
|  | } else { | 
|  | type = object.getClass(); | 
|  | } | 
|  | if (formalType.isAssignableFrom(type)) { | 
|  | return 1; | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Handle object | 
|  | if (formalType == Object.class) { | 
|  | return Integer.MAX_VALUE - 1; | 
|  | } | 
|  |  | 
|  | // Unsupported conversion | 
|  | return Integer.MAX_VALUE; | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("unchecked") | 
|  | @Override | 
|  | public <T> T convertLuaValue(LuaState luaState, int index, | 
|  | Class<T> formalType) { | 
|  | // Handle none | 
|  | LuaType luaType = luaState.type(index); | 
|  | if (luaType == null) { | 
|  | throw new IllegalArgumentException("undefined index: " + index); | 
|  | } | 
|  |  | 
|  | // Handle void | 
|  | if (formalType == Void.TYPE) { | 
|  | throw new ClassCastException(String.format( | 
|  | "cannot convert %s to %s", luaState.typeName(index), | 
|  | formalType.getCanonicalName())); | 
|  | } | 
|  |  | 
|  | // Handle Lua value proxy | 
|  | if (formalType == LuaValueProxy.class) { | 
|  | return (T) luaState.getProxy(index); | 
|  | } | 
|  |  | 
|  | // Handle Lua types | 
|  | switch (luaType) { | 
|  | case NIL: | 
|  | return null; | 
|  | case BOOLEAN: | 
|  | LuaValueConverter<?> luaValueConverter; | 
|  | luaValueConverter = LUA_VALUE_CONVERTERS.get(formalType); | 
|  | if (luaValueConverter != null) { | 
|  | return (T) luaValueConverter.convert(luaState, index); | 
|  | } | 
|  | if (formalType == Object.class) { | 
|  | return (T) Boolean.valueOf(luaState.toBoolean(index)); | 
|  | } | 
|  | break; | 
|  | case NUMBER: | 
|  | luaValueConverter = LUA_VALUE_CONVERTERS.get(formalType); | 
|  | if (luaValueConverter != null) { | 
|  | return (T) luaValueConverter.convert(luaState, index); | 
|  | } | 
|  | if (formalType == Object.class) { | 
|  | return (T) Double.valueOf(luaState.toNumber(index)); | 
|  | } | 
|  | break; | 
|  | case STRING: | 
|  | luaValueConverter = LUA_VALUE_CONVERTERS.get(formalType); | 
|  | if (luaValueConverter != null) { | 
|  | return (T) luaValueConverter.convert(luaState, index); | 
|  | } | 
|  | if (formalType == Object.class) { | 
|  | return (T) luaState.toString(index); | 
|  | } | 
|  | break; | 
|  | case TABLE: | 
|  | if (formalType == Map.class || formalType == Object.class) { | 
|  | final LuaValueProxy luaValueProxy = luaState.getProxy(index); | 
|  | return (T) new AbstractTableMap<Object>() { | 
|  | @Override | 
|  | protected Object convertKey(int index) { | 
|  | return getLuaState().toJavaObject(index, Object.class); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public LuaState getLuaState() { | 
|  | return luaValueProxy.getLuaState(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void pushValue() { | 
|  | luaValueProxy.pushValue(); | 
|  | } | 
|  | }; | 
|  | } | 
|  | if (formalType == List.class) { | 
|  | final LuaValueProxy luaValueProxy = luaState.getProxy(index); | 
|  | return (T) new AbstractTableList() { | 
|  | @Override | 
|  | public LuaState getLuaState() { | 
|  | return luaValueProxy.getLuaState(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void pushValue() { | 
|  | luaValueProxy.pushValue(); | 
|  | } | 
|  | }; | 
|  | } | 
|  | if (formalType.isArray()) { | 
|  | int length = luaState.rawLen(index); | 
|  | Class<?> componentType = formalType.getComponentType(); | 
|  | Object array = Array.newInstance(formalType.getComponentType(), | 
|  | length); | 
|  | for (int i = 0; i < length; i++) { | 
|  | luaState.rawGet(index, i + 1); | 
|  | try { | 
|  | Array.set(array, i, | 
|  | convertLuaValue(luaState, -1, componentType)); | 
|  | } finally { | 
|  | luaState.pop(1); | 
|  | } | 
|  | } | 
|  | return (T) array; | 
|  | } | 
|  | break; | 
|  | case FUNCTION: | 
|  | if (luaState.isJavaFunction(index)) { | 
|  | if (formalType == JavaFunction.class | 
|  | || formalType == Object.class) { | 
|  | return (T) luaState.toJavaFunction(index); | 
|  | } | 
|  | } | 
|  | break; | 
|  | case USERDATA: | 
|  | Object object = luaState.toJavaObjectRaw(index); | 
|  | if (object != null) { | 
|  | if (object instanceof TypedJavaObject) { | 
|  | TypedJavaObject typedJavaObject = (TypedJavaObject) object; | 
|  | if (typedJavaObject.isStrong()) { | 
|  | if (formalType.isAssignableFrom(typedJavaObject | 
|  | .getClass())) { | 
|  | return (T) typedJavaObject; | 
|  | } | 
|  | } | 
|  | return (T) ((TypedJavaObject) object).getObject(); | 
|  | } else { | 
|  | return (T) object; | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Handle object | 
|  | if (formalType == Object.class) { | 
|  | return (T) luaState.getProxy(index); | 
|  | } | 
|  |  | 
|  | // Unsupported conversion | 
|  | throw new ClassCastException(String.format("cannot convert %s to %s", | 
|  | luaState.typeName(index), formalType.getCanonicalName())); | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("unchecked") | 
|  | @Override | 
|  | public void convertJavaObject(LuaState luaState, Object object) { | 
|  | // Handle null | 
|  | if (object == null) { | 
|  | luaState.pushNil(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Handle known Java types | 
|  | JavaObjectConverter<Object> javaObjectConverter = (JavaObjectConverter<Object>) JAVA_OBJECT_CONVERTERS | 
|  | .get(object.getClass()); | 
|  | if (javaObjectConverter != null) { | 
|  | javaObjectConverter.convert(luaState, object); | 
|  | return; | 
|  | } | 
|  | if (object instanceof JavaFunction) { | 
|  | luaState.pushJavaFunction((JavaFunction) object); | 
|  | return; | 
|  | } | 
|  | if (object instanceof LuaValueProxy) { | 
|  | LuaValueProxy luaValueProxy = (LuaValueProxy) object; | 
|  | if (!luaValueProxy.getLuaState().equals(luaState)) { | 
|  | throw new IllegalArgumentException( | 
|  | "Lua value proxy is from a different Lua state"); | 
|  | } | 
|  | luaValueProxy.pushValue(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Push as is | 
|  | luaState.pushJavaObjectRaw(object); | 
|  | } | 
|  |  | 
|  | // -- Nested types | 
|  | /** | 
|  | * Converts Lua values. | 
|  | */ | 
|  | private interface LuaValueConverter<T> { | 
|  | /** | 
|  | * Converts a Lua value to a Java object. | 
|  | */ | 
|  | public T convert(LuaState luaState, int index); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Converts Java object. | 
|  | */ | 
|  | private interface JavaObjectConverter<T> { | 
|  | /** | 
|  | * Converts a Java object to a Lua value. | 
|  | */ | 
|  | public void convert(LuaState luaState, T object); | 
|  | } | 
|  | } |