jp.terasoluna.fw.util.GenericsUtil.java Source code

Java tutorial

Introduction

Here is the source code for jp.terasoluna.fw.util.GenericsUtil.java

Source

/*
 * Copyright (c) 2007 NTT DATA Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jp.terasoluna.fw.util;

import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * <code>Generics</code>????
 */
public class GenericsUtil {
    /**
     * 
     */
    private static final Log log = LogFactory.getLog(GenericsUtil.class);

    /**
     * ?????
     * <p>
     * <h5>??????</h5> ?????????????? ?????? ?????????? ?<code>WildCardType</code>?
     * ????????????? ???????????
     * <ul>
     * <li>????(???????) <code><pre>
     *     public class Descendant extends Generic&lt;String, Integer&gt; {
     *        ...
     *     }
     *     </pre></code> ???????[<code>String</code>, <code>Integer</code>]? ??????</li>
     * <li>????(????????) <code><pre>
     *     public class Descendant extends Generic&lt;String[], Integer&gt; {
     *        ...
     *     }
     *     </pre></code> ???????[<code>String[]</code>, <code>Integer</code>]? ??????</li>
     * <li>????(?????????) <code><pre>
     *     public class Descendant
     *         extends Generic&lt;String[], Map&lt;String, Object&gt;&gt; {
     *        ...
     *     }
     *     </pre></code> ???????[<code>String[]</code>, <code>Map</code>]? ??????</li>
     * <li>??????(???????) <code><pre>
     *     public class Descendant&lt;P, Q&gt; extends Generic&lt;S, T&gt; {
     *        ...
     *     }
     *     </pre></code></li>
     * <li>??????(???????) <code><pre>
     *     public class Descendant&lt;P extends String, Q super Bean&gt;
     *         extends Generic&lt;S, T&gt; {
     *        ...
     *     }
     *     </pre></code></li>
     * <li>??????(?????) <code><pre>
     *     Generic&lt;String, Integer&gt; descendant =
     *         new Generic&lt;String, Integer&gt;();
     *     </pre></code></li>
     * </ul>
     * </p>
     * <p>
     * <h5>??</h5> <code>genericType</code>?<code>descendantClass</code>?? ??????????????
     * <ul>
     * <li>? <code><pre>
     *       public class Child&lt;S, T&gt; extends Generic&lt;S, T&gt; {
     *          ...
     *       }
     *
     *       public class GrandChild&lt;S, T&gt; extends Child&lt;S, T&gt; {
     *          ...
     *       }
     *
     *       public class Descendant extends GrandChild&lt;String, Integer&gt; {
     *          ...
     *       }
     * </pre></code> ???????[<code>String</code>, <code>Integer</code>]? ??????</li>
     * </ul>
     * </p>
     * <p>
     * <h5>?????</h5> <code>genericType</code>?<code>descendantClass</code>??? ???????????
     * <code>genercClass</code>?????????
     * <ul>
     * <li>???? <code><pre>
     *       public class Generic&lt;S, T&gt; {
     *          ...
     *       }
     *
     *       public class Child&lt;A, T, B, S&gt; extends Generic&lt;S, T&gt; {
     *          ...
     *       }
     *
     *       public class Descendant
     *            extends Generic&lt;Boolean, Integer, Double, String&gt; {
     *          ...
     *       }
     * </pre></code> ???????[<code>String</code>, <code>Integer</code>]? ??????</li>
     * </ul>
     * </p>
     * @param <T> ???
     * @param genericClass ??
     * @param descendantClass <code>genericsClass</code>?? ???
     * @return ??<code>Class</code>?? ?<code>genercClass</code>?????
     * @throws IllegalArgumentException <code>genericClass</code>? <code>null</code>?? <code>descendantClass</code>?
     *             <code>null</code>??
     * @throws IllegalStateException <code>descendantClass</code>??? ????????? <code>genercClass</code>
     *             ????? ?????
     */
    @SuppressWarnings("rawtypes")
    public static <T> Class[] resolveParameterizedClass(Class<T> genericClass, Class<? extends T> descendantClass)
            throws IllegalArgumentException, IllegalStateException {
        if (genericClass == null) {
            throw new IllegalArgumentException("Argument 'genericsClass' (" + Class.class.getName() + ") is null");
        }
        if (descendantClass == null) {
            throw new IllegalArgumentException("Argument 'descendantClass'(" + Class.class.getName() + ") is null");
        }

        List<ParameterizedType> ancestorTypeList = getAncestorTypeList(genericClass, descendantClass);

        ParameterizedType parameterizedType = ancestorTypeList.get(ancestorTypeList.size() - 1);
        // parameterizedType??? Type ??
        // AbstractBLogic<P, R>??P?R
        Type[] actualTypes = parameterizedType.getActualTypeArguments();

        // ??????
        Class[] actualClasses = new Class[actualTypes.length];
        for (int i = 0; i < actualTypes.length; i++) {
            // actualTypes[i]i?
            // ancestorList????
            actualClasses[i] = resolveTypeVariable(actualTypes[i], ancestorTypeList);
        }
        return actualClasses;
    }

    /**
     * ????
     * <p>
     * <h5>??????</h5> ?????????????? ?????? ?????????? ?<code>WildCardType</code>?
     * ????????????? ???????????
     * <ul>
     * <li>????(???????) <code><pre>
     *     public class Descendant extends Generic&lt;String, Integer&gt; {
     *        ...
     *     }
     *     </pre></code> ???????<code>String</code>???<code>Integer</code>? ????</li>
     * <li>????(????????) <code><pre>
     *     public class Descendant extends Generic&lt;String[], Integer&gt; {
     *        ...
     *     }
     *     </pre></code> ???????<code>String[]</code>???<code>Integer</code>? ????</li>
     * <li>????(?????????) <code><pre>
     *     public class Descendant
     *         extends Generic&lt;String[], Map&lt;String, Object&gt;&gt; {
     *        ...
     *     }
     *     </pre></code> ???????<code>String[]</code>???<code>Map</code> ?????</li>
     * <li>??????(???????) <code><pre>
     *     public class Descendant&lt;P, Q&gt; extends Generic&lt;S, T&gt; {
     *        ...
     *     }
     *     </pre></code></li>
     * <li>??????(???????) <code><pre>
     *     public class Descendant&lt;P extends String, Q super Bean&gt;
     *         extends Generic&lt;S, T&gt; {
     *        ...
     *     }
     *     </pre></code></li>
     * <li>??????(?????) <code><pre>
     *     Generic&lt;String, Integer&gt; descendant =
     *         new Generic&lt;String, Integer&gt;();
     *     </pre></code></li>
     * </ul>
     * </p>
     * <p>
     * <h5>??</h5> <code>genericType</code>?<code>descendantClass</code>?? ??????????????
     * <ul>
     * <li>? <code><pre>
     *       public class Child&lt;S, T&gt; extends Generic&lt;S, T&gt; {
     *          ...
     *       }
     *
     *       public class GrandChild&lt;S, T&gt; extends Child&lt;S, T&gt; {
     *          ...
     *       }
     *
     *       public class Descendant extends GrandChild&lt;String, Integer&gt; {
     *          ...
     *       }
     * </pre></code> ???????<code>String</code>???<code>Integer</code> ?????</li>
     * </ul>
     * </p>
     * <p>
     * <h5>?????</h5> <code>genericType</code>?<code>descendantClass</code>??? ???????????
     * <code>genercClass</code>?????????
     * <ul>
     * <li>???? <code><pre>
     *       public class Generic&lt;S, T&gt; {
     *          ...
     *       }
     *
     *       public class Child&lt;A, T, B, S&gt; extends Generic&lt;S, T&gt; {
     *          ...
     *       }
     *
     *       public class Descendant
     *            extends Generic&lt;Boolean, Integer, Double, String&gt; {
     *          ...
     *       }
     * </pre></code> ???????<code>String</code>???<code>Integer</code> ?????</li>
     * </ul>
     * </p>
     * @param <T> ???
     * @param genericClass ??
     * @param descendantClass <code>genericsClass</code>?? ???
     * @param index ?????
     * @return ??<code>Class</code>
     * @throws IllegalArgumentException <code>genericClass</code>? <code>null</code>?? <code>descendantClass</code>?
     *             <code>null</code>?? <code>index</code>?<code>0</code>???????? ????
     * @throws IllegalStateException <code>descendantClass</code>??? ????????? <code>genercClass</code>
     *             ????? ?????
     */
    public static <T> Class<? extends Object> resolveParameterizedClass(Class<T> genericClass,
            Class<? extends T> descendantClass, int index) throws IllegalArgumentException, IllegalStateException {
        if (genericClass == null) {
            throw new IllegalArgumentException("Argument 'genericsClass' (" + Class.class.getName() + ") is null");
        }
        if (descendantClass == null) {
            throw new IllegalArgumentException("Argument 'descendantClass'(" + Class.class.getName() + ") is null");
        }

        List<ParameterizedType> ancestorTypeList = getAncestorTypeList(genericClass, descendantClass);

        ParameterizedType parameterizedType = ancestorTypeList.get(ancestorTypeList.size() - 1);
        // parameterizedType??? Type ??
        // AbstractBLogic<P, R>??P?R
        Type[] actualTypes = parameterizedType.getActualTypeArguments();

        // ??????
        if (index < 0 || index >= actualTypes.length) {
            throw new IllegalArgumentException("Argument 'index'(" + Integer.toString(index)
                    + ") is out of bounds of" + " generics parameters");
        }

        // actualTypes[index]index?
        // ancestorList????
        return resolveTypeVariable(actualTypes[index], ancestorTypeList);
    }

    /**
     * ??????? <code>ParameterizedType</code>???
     * @param <T> ???
     * @param genericClass ??
     * @param descendantClass <code>genericsClass</code>?? ???
     * @return ??????? <code>ParameterizedType</code>?
     * @throws IllegalStateException <code>descendantClass</code>? ??????????? <code>genercClass</code>
     *             ????? ?????
     */
    protected static <T> List<ParameterizedType> getAncestorTypeList(Class<T> genericClass,
            Class<? extends T> descendantClass) throws IllegalStateException {
        List<ParameterizedType> ancestorTypeList = new ArrayList<ParameterizedType>();
        Class<?> clazz = descendantClass;
        boolean isInterface = genericClass.isInterface();

        while (clazz != null) {
            Type type = clazz.getGenericSuperclass();
            if (checkParameterizedType(type, genericClass, ancestorTypeList)) {
                break;
            }

            // ??????
            // ??????
            if (!isInterface) {
                clazz = clazz.getSuperclass();
                continue;
            }
            if (checkInterfaceAncestors(genericClass, ancestorTypeList, clazz)) {
                break;
            }

            // ???????????
            // ????????
            // ?????????????
            // ??????????
            // ???Generics?API?????????????
            // ?????????
            clazz = clazz.getSuperclass();
        }

        // ?????
        // AbstractBLogic<P, R>
        if (ancestorTypeList.isEmpty()) {
            throw new IllegalStateException(
                    "Argument 'genericClass'(" + genericClass.getName() + ") does not declare type parameter");
        }

        // ???????????????
        // ??????????
        // ???Generics?API?????????????
        // ?????????
        ParameterizedType targetType = ancestorTypeList.get(ancestorTypeList.size() - 1);
        if (!targetType.getRawType().equals(genericClass)) {
            throw new IllegalStateException("Class(" + descendantClass.getName()
                    + ") is not concrete class of Class(" + genericClass.getName() + ")");
        }
        return ancestorTypeList;
    }

    /**
     * ?????? <code>ParameterizedType</code>???
     * @param <T> ???
     * @param genericClass ??
     * @param ancestorTypeList <code>ParameterizedType</code> ?
     * @param clazz ?
     * @return ?????????<code>true</code> ????????<code>false</code>
     */
    protected static <T> boolean checkInterfaceAncestors(Class<T> genericClass,
            List<ParameterizedType> ancestorTypeList, Class<?> clazz) {
        boolean genericTypeFound = false;
        Type[] interfaceTypes = clazz.getGenericInterfaces();
        for (Type interfaceType : interfaceTypes) {
            genericTypeFound = checkParameterizedType(interfaceType, genericClass, ancestorTypeList);
            if (genericTypeFound) {
                return true;
            }
            @SuppressWarnings("rawtypes")
            Class[] interfaces = clazz.getInterfaces();
            for (Class<?> interfaceClass : interfaces) {
                if (checkInterfaceAncestors(genericClass, ancestorTypeList, interfaceClass)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * <code>Type</code>???<code>ParameterizedType</code> ????????????
     * @param <T> ???
     * @param type ?
     * @param genericClass ??
     * @param ancestorTypeList <code>ParameterizedType</code> ?
     * @return <code>type</code>????????
     */
    protected static <T> boolean checkParameterizedType(Type type, Class<T> genericClass,
            List<ParameterizedType> ancestorTypeList) {
        if (!(type instanceof ParameterizedType)) {
            return false;
        }

        // ParameterizedType?????ParameterizedType
        // ??
        ParameterizedType parameterlizedType = (ParameterizedType) type;

        // ?Generics????ParameterizedType?
        // ????????
        // ???????????????????
        // ??????????
        // ???Generics?API?????????????
        // ?????????
        if (!genericClass.isAssignableFrom((Class<?>) parameterlizedType.getRawType())) {
            return false;
        }
        ancestorTypeList.add(parameterlizedType);

        // #getRawType???Type??
        if (parameterlizedType.getRawType().equals(genericClass)) {
            return true;
        }
        return false;
    }

    /**
     * ??<code>Type</code>??
     * @param type ????<code>Type</code>
     * @param ancestorTypeList <code>type</code>??? ??????<code>ParameterizedType</code>?
     * @return ?
     * @throws IllegalStateException <code>type</code>? <code>Class</code>???? <code>TypeVariable</code>????? 
     *             <code>type</code>?? ????????? <code>type</code>???<code>Class</code>????
     *             (??)?
     */
    protected static Class<? extends Object> resolveTypeVariable(Type type,
            List<ParameterizedType> ancestorTypeList) throws IllegalStateException {

        if (isNotTypeVariable(type)) {
            return getRawClass(type);
        }

        // TypeVariable:?
        TypeVariable<?> targetType = (TypeVariable<?>) type;
        Type actualType = null;
        for (int i = ancestorTypeList.size() - 1; i >= 0; i--) {
            ParameterizedType ancestorType = ancestorTypeList.get(i);

            // ?????
            GenericDeclaration declaration = targetType.getGenericDeclaration();
            if (!(declaration instanceof Class)) {
                throw new IllegalStateException("TypeVariable(" + targetType.getName()
                        + " is not declared at Class " + "(ie. is declared at Method or Constructor)");
            }

            // ?Generics????????
            Class<?> declaredClass = (Class<?>) declaration;
            if (declaredClass != ancestorType.getRawType()) {
                continue;
            }

            // ????????
            // ConcreteAbstractBLogic<R,P> extends AbstractBLogic<P,R>
            // ????????type????
            Type[] parameterTypes = declaredClass.getTypeParameters();
            int index = ArrayUtils.indexOf(parameterTypes, targetType);
            if (index == -1) {
                // ???????????????
                // ??????????
                // ???Generics?API?????????????
                // ?????????
                throw new IllegalStateException("Class(" + declaredClass.getName()
                        + ") does not declare TypeValidable(" + targetType.getName() + ")");
            }
            actualType = ancestorType.getActualTypeArguments()[index];
            if (log.isDebugEnabled()) {
                log.debug("resolved " + targetType.getName() + " -> " + actualType);
            }

            if (isNotTypeVariable(actualType)) {
                return getRawClass(actualType);
            }
            targetType = (TypeVariable<?>) actualType;
        }

        throw new IllegalStateException(
                "Concrete type of Type(" + type + ") was not found in ancestorList(" + ancestorTypeList + ")");
    }

    /**
     * <code>type</code>?<code>Class</code> ????<code>TypeVariable</code>??
     * @param type <code>Type</code>
     * @return <code>type</code>? <code>Class, ParameterizedType, GenericArrayType</code>?? <code>true</code> 
     *         <code>type</code>?<code>TypeVariable</code>?? <code>false</code>
     * @throws IllegalStateException <code>type</code>? <code>Class</code>?<code>ParameterizedType</code>?
     *             <code>GenericArrayType</code>?<code>TypeVariable</code>? ??????
     */
    protected static boolean isNotTypeVariable(Type type) throws IllegalStateException {
        if (type instanceof Class) {
            return true;
        } else if (type instanceof TypeVariable) {
            return false;
        } else if (type instanceof ParameterizedType) {
            return true;
        } else if (type instanceof GenericArrayType) {
            return true;
        }
        throw new IllegalStateException("Type(" + type + ") is not instance of " + TypeVariable.class.getName()
                + ", " + ParameterizedType.class.getName() + ", " + GenericArrayType.class.getName() + " nor "
                + Class.class.getName());
    }

    /**
     * <code>type</code>????
     * @param type <code>Type</code>
     * @return <code>Class</code>
     * @throws IllegalStateException <code>type</code>? <code>Class</code>?<code>ParameterizedType</code>?
     *             <code>GenericArrayType</code>???????
     */
    @SuppressWarnings("unchecked")
    protected static Class<? extends Object> getRawClass(Type type) throws IllegalStateException {
        if (type instanceof Class) {
            return (Class<? extends Object>) type;
        } else if (type instanceof ParameterizedType) {
            return getRawClass(((ParameterizedType) type).getRawType());
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            Class<? extends Object> componentClass = getRawClass(componentType);
            return Array.newInstance(componentClass, 0).getClass();
        }
        throw new IllegalStateException("Type(" + type + ") is not instance of " + ParameterizedType.class.getName()
                + ", " + GenericArrayType.class.getName() + " nor " + Class.class.getName());
    }

}