All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nl.weeaboo.lua2.luajava.JavaClass Maven / Gradle / Ivy

package nl.weeaboo.lua2.luajava;

import static nl.weeaboo.lua2.vm.LuaValue.valueOf;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;

import nl.weeaboo.lua2.LuaException;
import nl.weeaboo.lua2.io.IReadResolveSerializable;
import nl.weeaboo.lua2.io.IWriteReplaceSerializable;
import nl.weeaboo.lua2.io.LuaSerializable;
import nl.weeaboo.lua2.vm.LuaString;
import nl.weeaboo.lua2.vm.LuaUserdata;
import nl.weeaboo.lua2.vm.LuaValue;
import nl.weeaboo.lua2.vm.Varargs;

@LuaSerializable
final class JavaClass implements IWriteReplaceSerializable {

    private static final long serialVersionUID = 1L;

    private static final Comparator methodSorter = new MethodSorter();

    private final Class clazz;
    private final boolean isArray;

    private transient ClassMetaTable metaTable;

    private transient JavaConstructor[] constrs;
    private transient Map fields;
    private transient Map methods;

    public JavaClass(Class c) {
        clazz = c;
        isArray = c.isArray();
    }

    @Override
    public Object writeReplace() {
        return new JavaClassRef(clazz);
    }

    public LuaUserdata newInstance(Varargs luaArgs) throws IllegalArgumentException, InstantiationException,
            IllegalAccessException, InvocationTargetException {

        JavaConstructor constr = findConstructor(luaArgs);
        if (constr == null) {
            throw new LuaException("No suitable constructor found for: " + clazz.getName());
        }

        List> paramTypes = constr.getParamTypes();
        Object[] javaArgs = new Object[paramTypes.size()];
        CoerceLuaToJava.coerceArgs(javaArgs, luaArgs, paramTypes);

        Object javaObject = constr.newInstance(javaArgs);

        return LuaUserdata.userdataOf(javaObject, getMetatable());
    }

    @Override
    public int hashCode() {
        return clazz.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof JavaClass) {
            JavaClass ci = (JavaClass)obj;
            return clazz.equals(ci.clazz);
        }
        return false;
    }

    public Class getWrappedClass() {
        return clazz;
    }

    public boolean isArray() {
        return isArray;
    }

    protected @Nullable JavaConstructor findConstructor(Varargs luaArgs) {
        JavaConstructor bestMatch = null;
        int bestScore = Integer.MAX_VALUE;

        for (JavaConstructor constr : getConstructors()) {
            int score = CoerceLuaToJava.scoreParamTypes(luaArgs, constr.getParamTypes());
            if (score == 0) {
                return constr; // Perfect match, return at once
            } else if (score < bestScore) {
                bestScore = score;
                bestMatch = constr;
            }
        }

        return bestMatch;
    }

    public JavaConstructor[] getConstructors() {
        if (constrs == null) {
            Constructor[] cs = clazz.getConstructors();

            JavaConstructor[] result = new JavaConstructor[cs.length];
            for (int n = 0; n < cs.length; n++) {
                result[n] = new JavaConstructor(cs[n]);
            }
            constrs = result;
        }
        return constrs;
    }

    public ClassMetaTable getMetatable() {
        if (metaTable == null) {
            metaTable = new ClassMetaTable(this);
        }
        return metaTable;
    }

    public @Nullable Field getField(LuaValue name) {
        if (fields == null) {
            fields = new HashMap<>();
            for (Field f : clazz.getFields()) {
                fields.put(valueOf(f.getName()), f);
            }
        }
        return fields.get(name);
    }

    public JavaMethod[] getMethods(LuaValue name) {
        if (methods == null) {
            Method[] marr = clazz.getMethods();
            Arrays.sort(marr, methodSorter);

            methods = new HashMap<>();

            String curName = null;
            List list = new ArrayList<>();
            for (Method m : marr) {
                // Workaround for https://bugs.openjdk.java.net/browse/JDK-4283544
                m.setAccessible(true);

                if (!m.getName().equals(curName)) {
                    if (curName != null) {
                        methods.put(valueOf(curName), list.toArray(new JavaMethod[list.size()]));
                    }
                    curName = m.getName();
                    list.clear();
                }
                list.add(new JavaMethod(m));
            }

            if (curName != null) {
                methods.put(LuaString.valueOf(curName), list.toArray(new JavaMethod[list.size()]));
            }
        }
        return methods.get(name);
    }

    public boolean hasMethod(LuaValue name) {
        return getMethods(name) != null;
    }

    @LuaSerializable
    private static class JavaClassRef implements IReadResolveSerializable {

        private static final long serialVersionUID = 1L;

        private final Class clazz;

        public JavaClassRef(Class clazz) {
            this.clazz = clazz;
        }

        @Override
        public Object readResolve() {
            return LuajavaLib.getClassInfo(clazz);
        }
    }

    private static final class MethodSorter implements Comparator {

        @Override
        public int compare(Method m1, Method m2) {
            return m1.getName().compareTo(m2.getName());
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy