Java tutorial
/* * Copyright (C) 2010-2015, Danilo Pianini and contributors * listed in the project's pom.xml file. * * This file is part of Alchemist, and is distributed under the terms of * the GNU General Public License, with a linking exception, as described * in the file LICENSE in the Alchemist distribution's top directory. */ package it.unibo.alchemist.language.protelis.util; import it.unibo.alchemist.language.protelis.java7.util.L; import it.unibo.alchemist.language.protelis.java7.util.PrimitiveUtils; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.tuple.ImmutableTriple; import org.apache.commons.lang3.tuple.Triple; import org.apache.commons.math3.util.Pair; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; /** * Utilities that make easier to cope with Java Reflection. * * @author Danilo Pianini * */ public final class ReflectionUtils { private static final int CACHE_MAX_SIZE = 1000; private static final LoadingCache<Triple<Class<?>, String, List<Class<?>>>, Method> METHOD_CACHE = CacheBuilder .newBuilder().maximumSize(CACHE_MAX_SIZE).expireAfterAccess(1, TimeUnit.HOURS) .build(new CacheLoader<Triple<Class<?>, String, List<Class<?>>>, Method>() { @Override public Method load(final Triple<Class<?>, String, List<Class<?>>> key) throws Exception { final List<Class<?>> al = key.getRight(); final Class<?>[] args = new Class<?>[al.size()]; return loadBestMethod(key.getLeft(), key.getMiddle(), al.toArray(args)); } }); private ReflectionUtils() { } /** * @param methodName * the method to be invoked * @param target * the target object. It can not be null * @param args * the arguments for the method * @return the result of the invocation, or an {@link IllegalStateException} * if something goes wrong. */ public static Object invokeBestNotStatic(final Object target, final String methodName, final Object[] args) { Objects.requireNonNull(target); return invokeBestMethod(target.getClass(), methodName, target, args); } /** * @param clazz * the class where to search for suitable methods * @param methodName * the method to be invoked * @param args * the arguments for the method * @return the result of the invocation, or an {@link IllegalStateException} * if something goes wrong. */ public static Object invokeBestStatic(final Class<?> clazz, final String methodName, final Object[] args) { return invokeBestMethod(clazz, methodName, null, args); } /** * @param clazz * the class where to search for suitable methods * @param methodName * the method to be invoked * @param args * the arguments for the method * @return the result of the invocation, or an {@link IllegalStateException} * if something goes wrong. */ public static Method searchBestMethod(final Class<?> clazz, final String methodName, final Object... args) { return searchBestMethod(clazz, methodName, Arrays.asList(args)); } /** * @param clazz * the class where to search for suitable methods * @param methodName * the method to be invoked * @param args * the arguments for the method * @return the result of the invocation, or an {@link IllegalStateException} * if something goes wrong. */ public static Method searchBestMethod(final Class<?> clazz, final String methodName, final List<Object> args) { final List<Class<?>> argClass = new ArrayList<>(); for (Object i : args) { argClass.add(i.getClass()); } try { return METHOD_CACHE.get((Triple) new ImmutableTriple<>(clazz, methodName, argClass)); } catch (ExecutionException e) { throw new NoSuchMethodError(methodName + "/" + args.size() + argClass + " does not exist in " + clazz + ". You tried to invoke it with arguments " + args); } } private static Method loadBestMethod(final Class<?> clazz, final String methodName, final Class<?>[] argClass) { Objects.requireNonNull(clazz, "The class on which the method will be invoked can not be null."); Objects.requireNonNull(methodName, "Method name can not be null."); Objects.requireNonNull(argClass, "Method arguments can not be null."); /* * If there is a matching method, return it */ try { return clazz.getMethod(methodName, argClass); } catch (NoSuchMethodException | SecurityException e) { /* * Look it up on the cache */ /* * Deal with Java method overloading scoring methods */ final Method[] candidates = clazz.getMethods(); final List<Pair<Integer, Method>> lm = new ArrayList<>(candidates.length); for (final Method m : candidates) { // TODO: Workaround for different Method API if (m.getParameterTypes().length == argClass.length && methodName.equals(m.getName())) { final Class<?>[] params = m.getParameterTypes(); int p = 0; boolean compatible = true; for (int i = 0; compatible && i < argClass.length; i++) { final Class<?> expected = params[i]; if (expected.isAssignableFrom(argClass[i])) { /* * No downcast required, there is compatibility */ p++; } else if (!PrimitiveUtils.classIsNumber(expected)) { compatible = false; } } if (compatible) { lm.add(new Pair<>(p, m)); } } } /* * Find best */ if (lm.size() > 0) { Pair<Integer, Method> max = lm.get(0); for (Pair<Integer, Method> cPair : lm) { if (cPair.getFirst().compareTo(max.getFirst()) > 0) { max = cPair; } } return max.getSecond(); } } List<Class<?>> list = new ArrayList<>(); for (Class<?> c : argClass) { list.add(c); } final String argType = list.toString(); throw new NoSuchMethodError( methodName + "/" + argClass.length + argType + " does not exist in " + clazz + "."); } /** * @param clazz * the class where to search for suitable methods * @param methodName * the method to be invoked * @param target * the target object. It can be null, if the method which is * being invoked is static * @param args * the arguments for the method * @return the result of the invocation, or an {@link IllegalStateException} * if something goes wrong. */ public static Object invokeBestMethod(final Class<?> clazz, final String methodName, final Object target, final Object[] args) { return invokeMethod(searchBestMethod(clazz, methodName, args), target, args); } /** * @param method * the methods to invoke * @param target * the target object. It can be null, if the method which is * being invoked is static * @param args * the arguments for the method * @return the result of the invocation, or an {@link IllegalStateException} * if something goes wrong. */ public static Object invokeMethod(final Method method, final Object target, final Object[] args) { final Class<?>[] params = method.getParameterTypes(); List<Object> list = new ArrayList<Object>(); for (int i = 0; i < args.length; i++) { final Class<?> expected = params[i]; final Object actual = args[i]; if (!expected.isAssignableFrom(actual.getClass()) && PrimitiveUtils.classIsNumber(expected)) { // TODO: Removed .get() list.add(PrimitiveUtils.castIfNeeded(expected, (Number) actual)); // list.add(PrimitiveUtils.castIfNeeded(expected, (Number) actual).get()); } else { list.add(actual); } } Object[] actualArgs = list.toArray(); try { return method.invoke(target, actualArgs); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { L.error(e); throw new IllegalStateException( "Cannot invoke " + method + " with arguments " + Arrays.toString(args) + " on " + target, e); } } }