serene-simple/src/main/java/serene/simple/builtin/Reflector.java

158 lines
5.9 KiB
Java

/**
* Serene (simple) - A PoC lisp to collect data on Serenes concepts
* Copyright (C) 2019-2020 Sameer Rahmani <lxsameer@gnu.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/**
* I have borrowed the foundations of this namespace from Clojure
* Which is published under EPL license.
*/
package serene.simple.builtin;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;
import java.util.ArrayList;
import serene.simple.SereneException;
public class Reflector {
static Object boxArg(Class<?> paramType, Object arg) {
if (!paramType.isPrimitive())
return paramType.cast(arg);
else if (paramType == boolean.class)
return Boolean.class.cast(arg);
else if (paramType == char.class)
return Character.class.cast(arg);
else if (arg instanceof Number) {
Number n = (Number) arg;
if (paramType == int.class)
return n.intValue();
else if (paramType == float.class)
return n.floatValue();
else if (paramType == double.class)
return n.doubleValue();
else if (paramType == long.class)
return n.longValue();
else if (paramType == short.class)
return n.shortValue();
else if (paramType == byte.class)
return n.byteValue();
}
throw new IllegalArgumentException(
"Unexpected param type, expected: " + paramType + ", given: " + arg.getClass().getName());
}
static Object[] boxArgs(Class<?>[] params, Object[] args) {
if (params.length == 0)
return null;
Object[] ret = new Object[params.length];
for (int i = 0; i < params.length; i++) {
Object arg = args[i];
Class<?> paramType = params[i];
ret[i] = boxArg(paramType, arg);
}
return ret;
}
public static Object invokeConstructor(Class<?> c, Object[] args) {
try {
Constructor<?>[] allctors = c.getConstructors();
ArrayList<Object> ctors = new ArrayList<Object>();
for (int i = 0; i < allctors.length; i++) {
Constructor<?> ctor = allctors[i];
if (ctor.getParameterTypes().length == args.length)
ctors.add(ctor);
}
if (ctors.isEmpty()) {
throw new IllegalArgumentException("No matching ctor found" + " for " + c);
} else if (ctors.size() == 1) {
Constructor<?> ctor = (Constructor<?>) ctors.get(0);
return ctor.newInstance(boxArgs(ctor.getParameterTypes(), args));
} else // overloaded w/same arity
{
for (Iterator iterator = ctors.iterator(); iterator.hasNext();) {
Constructor<?> ctor = (Constructor<?>) iterator.next();
Class<?>[] params = ctor.getParameterTypes();
if (isCongruent(params, args)) {
Object[] boxedArgs = boxArgs(params, args);
return ctor.newInstance(boxedArgs);
}
}
throw new IllegalArgumentException("No matching ctor found" + " for " + c);
}
} catch (Exception e) {
throw new SereneException(
String.format("Can't intantiate '%s': %s", c.getName(), e.getMessage()));
}
}
static public boolean paramArgTypeMatch(Class<?> paramType, Class<?> argType) {
if (argType == null)
return !paramType.isPrimitive();
if (paramType == argType || paramType.isAssignableFrom(argType))
return true;
if (paramType == int.class)
return argType == Integer.class || argType == long.class || argType == Long.class || argType == short.class
|| argType == byte.class;// || argType == FixNum.class;
else if (paramType == float.class)
return argType == Float.class || argType == double.class;
else if (paramType == double.class)
return argType == Double.class || argType == float.class;// || argType == DoubleNum.class;
else if (paramType == long.class)
return argType == Long.class || argType == int.class || argType == short.class || argType == byte.class;// ||
// argType
// ==
// BigNum.class;
else if (paramType == char.class)
return argType == Character.class;
else if (paramType == short.class)
return argType == Short.class;
else if (paramType == byte.class)
return argType == Byte.class;
else if (paramType == boolean.class)
return argType == Boolean.class;
return false;
}
static boolean isCongruent(Class<?>[] params, Object[] args) {
boolean ret = false;
if (args == null)
return params.length == 0;
if (params.length == args.length) {
ret = true;
for (int i = 0; ret && i < params.length; i++) {
Object arg = args[i];
Class<?> argType = (arg == null) ? null : arg.getClass();
Class<?> paramType = params[i];
ret = paramArgTypeMatch(paramType, argType);
}
}
return ret;
}
}