net.femtoparsec.jnlmin.utils.ReflectUtils.java Source code

Java tutorial

Introduction

Here is the source code for net.femtoparsec.jnlmin.utils.ReflectUtils.java

Source

/*
 * @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;
    }

}