158 lines
5.9 KiB
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;
|
|
}
|
|
|
|
}
|