All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jopendocument.util.ReflectUtils Maven / Gradle / Ivy

Go to download

jOpenDocument is a free library for developers looking to use Open Document files without OpenOffice.org.

The newest version!
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2008-2013 jOpenDocument, by ILM Informatique. All rights reserved.
 * 
 * The contents of this file are subject to the terms of the GNU
 * General Public License Version 3 only ("GPL").  
 * You may not use this file except in compliance with the License. 
 * You can obtain a copy of the License at http://www.gnu.org/licenses/gpl-3.0.html
 * See the License for the specific language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each file.
 * 
 */

package org.jopendocument.util;

import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public final class ReflectUtils {

    static private Map resolveTypes(Class c, Class raw) {
        final Map res = new HashMap();
        if (!raw.isAssignableFrom(c))
            return res;

        // c : ListDeString implements List
        final List types = new ArrayList(Arrays.asList(c.getGenericInterfaces()));
        types.add(c.getGenericSuperclass());
        for (final Type t : types) {
            if (t instanceof ParameterizedType) {
                // eg List
                final ParameterizedType pt = (ParameterizedType) t;
                if (raw.isAssignableFrom((Class) pt.getRawType())) {
                    // eg List.class (List)
                    final Class rawType = (Class) pt.getRawType();
                    // eg [String.class]
                    final Type[] actualTypeArguments = pt.getActualTypeArguments();
                    // eg [E]
                    final TypeVariable[] typeParameters = rawType.getTypeParameters();
                    for (int i = 0; i < actualTypeArguments.length; i++) {
                        res.put(typeParameters[i], actualTypeArguments[i]);
                    }
                }
            }
            final Class tc = getClass(t);
            if (tc != null) {
                res.putAll(resolveTypes(tc, raw));
            }
        }
        return res;
    }

    /**
     * The map of type arguments of baseClass to actual type for childClass.
     * 
     * @param  the type of the baseClass.
     * @param childClass the class to test, eg Props.class with Props extends Map.
     * @param baseClass the generic superclass, eg Map.class.
     * @return a the map, eg {K => String.class, V => null}.
     */
    public static  Map>, Class> getTypeArgumentsMap(Class childClass, Class baseClass) {
        final TypeVariable>[] actualTypeArguments = baseClass.getTypeParameters();
        if (actualTypeArguments.length == 0)
            throw new IllegalArgumentException(baseClass + " is not generic");

        final Map>, Class> res = new LinkedHashMap>, Class>();
        final Map resolvedTypes = resolveTypes(childClass, baseClass);
        // for each actual type argument provided to baseClass, determine (if possible)
        // the raw class for that type argument.
        // resolve types by chasing down type variables.
        for (final TypeVariable> baseType : actualTypeArguments) {
            Type currentType = baseType;
            while (resolvedTypes.containsKey(currentType)) {
                currentType = resolvedTypes.get(currentType);
            }
            res.put(baseType, getClass(currentType));
        }

        return res;
    }

    /**
     * Search for the list of class used to extend/implement a generic class/interface.
     * 
     * @param  the type of the baseClass.
     * @param childClass the class to test, eg Props.class with Props extends Map.
     * @param baseClass the generic superclass, eg Map.class.
     * @return the list of actual classes w/o the possible nulls (if childClass is generic), never
     *         null, eg [Boolean.class].
     */
    public static  List> getTypeArguments(Class childClass, Class baseClass) {
        final ArrayList> res = new ArrayList>();
        // ok since getTypeArgumentsMap returns a LinkedHashMap
        for (final Class c : getTypeArgumentsMap(childClass, baseClass).values()) {
            if (c != null)
                res.add(c);
        }
        return res;
    }

    static public  List> getTypeArguments(U o, Class raw) {
        return getTypeArguments(o.getClass().asSubclass(raw), raw);
    }

    /**
     * Whether o can be casted to raw<typeArgs>.
     * 
     * @param  type of the superclass.
     * @param o the instance to check, eg new MapOfInt2Boolean().
     * @param raw the generic superclass, eg Map.class.
     * @param typeArgs arguments to raw, eg Integer.class, Boolean.class.
     * @return whether o is a raw<typeArgs>, eg true : new MapOfInt2Boolean()
     *         is a Map<Integer, Boolean>.
     */
    static public  boolean isCastable(U o, Class raw, Class... typeArgs) {
        return getTypeArguments(o, raw).equals(Arrays.asList(typeArgs));
    }

    // *** pasted from http://www.artima.com/weblogs/viewpost.jsp?thread=208860

    /**
     * Get the underlying class for a type, or null if the type is a variable type.
     * 
     * @param type the type
     * @return the underlying class
     */
    private static Class getClass(Type type) {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            return getClass(((ParameterizedType) type).getRawType());
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            Class componentClass = getClass(componentType);
            if (componentClass != null) {
                return Array.newInstance(componentClass, 0).getClass();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

}