/** * Serene (simple) - A PoC lisp to collect data on Serenes concepts * Copyright (C) 2019-2020 Sameer Rahmani * * 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 ctors = new ArrayList(); 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; } }