Java tutorial
/* * @copyright Copyright (c) 2012, Bastien Aracil * All rights reserved. * New BSD license. See http://en.wikipedia.org/wiki/Bsd_license * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The name of Bastien Aracil may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BASTIEN ARACIL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.femtoparsec.jnlmin.utils; import org.apache.commons.lang3.Validate; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; /** * @author Bastien Aracil * Date: 05/12/12 */ public class ReflectUtils { private static Map<Class<?>, Class<?>> PRIMITIVE_TO_BOXED_MAP = new HashMap<>(); private static Map<Class<? extends Number>, Integer> NUMBER_DISTANCE_MAP = new HashMap<>(); static { PRIMITIVE_TO_BOXED_MAP.put(Boolean.TYPE, Boolean.class); PRIMITIVE_TO_BOXED_MAP.put(Character.TYPE, Character.class); PRIMITIVE_TO_BOXED_MAP.put(Byte.TYPE, Byte.class); PRIMITIVE_TO_BOXED_MAP.put(Short.TYPE, Short.class); PRIMITIVE_TO_BOXED_MAP.put(Integer.TYPE, Integer.class); PRIMITIVE_TO_BOXED_MAP.put(Long.TYPE, Long.class); PRIMITIVE_TO_BOXED_MAP.put(Float.TYPE, Float.class); PRIMITIVE_TO_BOXED_MAP.put(Double.TYPE, Double.class); NUMBER_DISTANCE_MAP.put(Byte.class, 0); NUMBER_DISTANCE_MAP.put(Short.class, 1); NUMBER_DISTANCE_MAP.put(Integer.class, 2); NUMBER_DISTANCE_MAP.put(Long.class, 3); NUMBER_DISTANCE_MAP.put(Float.class, 4); NUMBER_DISTANCE_MAP.put(Double.class, 5); NUMBER_DISTANCE_MAP.put(Number.class, 6); } public static Map<String, Map<Class<?>, Method>> findUnaryMethods(Class<?> clazz) { Map<String, Map<Class<?>, Method>> result = new TreeMap<>(); Method[] methods = clazz.getMethods(); for (Method method : methods) { int modified = method.getModifiers(); if (Modifier.isPublic(modified) && method.getParameterTypes().length == 1) { Map<Class<?>, Method> methodMap = result.get(method.getName()); if (methodMap == null) { methodMap = new HashMap<>(); result.put(method.getName(), methodMap); } methodMap.put(method.getParameterTypes()[0], method); } } return result; } public static Class<?> findClosestClass(Class<?> clazz, Collection<Class<?>> classes) { int minDistance = 0; int distance; Class<?> result = null; for (Class<?> aClass : classes) { distance = findClassDistance(clazz, aClass); if (distance < 0) { continue; } if (result == null || distance < minDistance) { result = aClass; minDistance = distance; } } return result; } public static int findClassDistance(Class<?> lower, Class<?> upper) { lower = boxClass(lower); upper = boxClass(upper); if (lower == upper) { return 0; } if (Number.class.isAssignableFrom(lower) && Number.class.isAssignableFrom(upper)) { Integer lowerDistance = NUMBER_DISTANCE_MAP.get(lower.asSubclass(Number.class)); Integer upperDistance = NUMBER_DISTANCE_MAP.get(upper.asSubclass(Number.class)); if (lowerDistance != null && upperDistance != null) { if (lowerDistance > upperDistance) { return -1; } return upperDistance - lowerDistance; } //lower or upper is a custom Number (like BigInteger) //handle it as normal classes } if (!upper.isAssignableFrom(lower)) { return -1; } if (upper.isInterface()) { if (lower.isInterface()) { return findIIDistance(lower, upper); } else { return findCIDistance(lower, upper); } } else { assert !lower.isInterface(); return findCCDistance(lower, upper); } } private static int findCCDistance(Class<?> lower, Class<?> upper) { if (upper.isInterface() || lower.isInterface()) { throw new IllegalArgumentException( String.format("Invalid input class : cannot be interfaces. upper=%s lower=%s", upper, lower)); } if (lower == upper) { return 0; } if (!upper.isAssignableFrom(lower)) { return -1; } int distance = 0; while (lower != null && lower != upper) { lower = lower.getSuperclass(); distance++; } if (lower == null) { throw new RuntimeException("BUG"); } return distance; } private static int findIIDistance(Class<?> lower, Class<?> upper) { if (!upper.isInterface() || !lower.isInterface()) { throw new IllegalArgumentException( String.format("Invalid input class : cannot be interfaces. upper=%s lower=%s", upper, lower)); } if (lower == upper) { return 0; } if (!upper.isAssignableFrom(lower)) { return -1; } int distance = -1; Class<?>[] interfaces = lower.getInterfaces(); for (Class<?> anInterface : interfaces) { int tmp = findIIDistance(anInterface, upper); if (tmp >= 0) { tmp++; distance = distance < 0 ? tmp : Math.min(distance, tmp); } if (distance == 0) { //cannot do better break; } } return distance; } private static int findCIDistance(Class<?> lower, Class<?> upper) { if (!upper.isInterface() || lower.isInterface()) { throw new IllegalArgumentException( String.format("Invalid input class : upper=%s lower=%s", upper, lower)); } if (lower == upper) { return 0; } if (!upper.isAssignableFrom(lower)) { return -1; } int distance = -1; int offset = 0; do { int tmp = findCIOneLevelDistance(lower, upper); if (tmp >= 0) { tmp += offset + 1; distance = distance < 0 ? tmp : Math.min(distance, tmp); } if (distance == 0) { break; } lower = lower.getSuperclass(); offset++; } while (lower != null); return distance; } private static int findCIOneLevelDistance(Class<?> lower, Class<?> upper) { if (!upper.isInterface() || lower.isInterface()) { throw new IllegalArgumentException( String.format("Invalid input class : upper=%s lower=%s", upper, lower)); } if (lower == upper) { return 0; } if (!upper.isAssignableFrom(lower)) { return -1; } int distance = -1; Class<?>[] interfaces = lower.getInterfaces(); for (Class<?> anInterface : interfaces) { int tmp = findIIDistance(anInterface, upper); if (tmp >= 0) { distance = distance < 0 ? tmp : Math.min(distance, tmp); } if (distance == 0) { break; } } return distance; } private static Class<?> boxClass(Class<?> clazz) { if (clazz.isPrimitive()) { return Validate.notNull(PRIMITIVE_TO_BOXED_MAP.get(clazz)); } return clazz; } }