Package com.github.jmkgreen.morphia.utils

Source Code of com.github.jmkgreen.morphia.utils.ReflectionUtils

/**
* Copyright (C) 2010 Olafur Gauti Gudmundsson
*
* 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 com.github.jmkgreen.morphia.utils;

import com.github.jmkgreen.morphia.Key;
import com.github.jmkgreen.morphia.annotations.Embedded;
import com.github.jmkgreen.morphia.annotations.Entity;
import com.github.jmkgreen.morphia.mapping.MappingException;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.regex.Pattern;
import org.bson.types.CodeWScope;
import org.bson.types.ObjectId;

/**
* Various reflection utility methods, used mainly in the Mapper.
*
* @author Olafur Gauti Gudmundsson
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class ReflectionUtils {

    /**
     * Get an array of all fields declared in the supplied class, and all its
     * superclasses (except java.lang.Object).
     *
     * @param type              the class for which we want to retrieve the Fields
     * @param returnFinalFields specifies whether to return final fields
     * @return an array of all declared and inherited fields
     */
    public static Field[] getDeclaredAndInheritedFields(final Class type, final boolean returnFinalFields) {
        List<Field> allFields = new ArrayList<Field>();
        allFields.addAll(getValidFields(type.getDeclaredFields(), returnFinalFields));
        Class parent = type.getSuperclass();
        while ((parent != null) && (parent != Object.class)) {
            allFields.addAll(getValidFields(parent.getDeclaredFields(), returnFinalFields));
            parent = parent.getSuperclass();
        }
        return allFields.toArray(new Field[allFields.size()]);
    }

    /**
     * Get a list of all methods declared in the supplied class, and all its
     * superclasses (except java.lang.Object), recursively.
     *
     * @param type the class for which we want to retrieve the Methods
     * @return an array of all declared and inherited fields
     */
    public static List<Method> getDeclaredAndInheritedMethods(final Class type) {
        return getDeclaredAndInheritedMethods(type, new ArrayList());
    }

    protected static List<Method> getDeclaredAndInheritedMethods(final Class type, List<Method> methods) {
        if ((type == null) || (type == Object.class)) {
            return methods;
        }
        if (methods == null) {
            methods = new ArrayList<Method>();
        }

        Class parent = type.getSuperclass();
        methods = getDeclaredAndInheritedMethods(parent, methods);

        for (Method m : type.getDeclaredMethods()) {
            if (!Modifier.isStatic(m.getModifiers())) {
                methods.add(m);
            }
        }

        return methods;
    }

    public static List<Field> getValidFields(final Field[] fields, final boolean returnFinalFields) {
        List<Field> validFields = new ArrayList<Field>();
        // we ignore static and final fields
        for (Field field : fields) {
            if (!Modifier.isStatic(field.getModifiers())
                    && (returnFinalFields || !Modifier.isFinal(field.getModifiers()))) {
                validFields.add(field);
            }
        }
        return validFields;
    }

//    public static boolean implementsAnyInterface(final Class type, final Class... interfaceClasses)
//    {
//        for (Class iF : interfaceClasses)
//        {
//            if (implementsInterface(type, iF))
//            {
//                return true;
//            }
//        }
//        return false;
//    }

    /**
     * Check if a class implements a specific interface.
     *
     * @param type           the class we want to check
     * @param interfaceClass the interface class we want to check against
     * @return true if type implements interfaceClass, else false
     */
    public static boolean implementsInterface(final Class type, final Class interfaceClass) {
        return interfaceClass.isAssignableFrom(type);
    }

    /**
     * Check if a class extends a specific class.
     *
     * @param type
     *            the class we want to check
     * @param superClass
     *            the super class we want to check against
     * @return true if type implements superClass, else false
     */
//    public static boolean extendsClass(final Class type, final Class superClass)
//    {
//        return superClass.isAssignableFrom(type);
//    }

    /**
     * Check if the class supplied represents a valid property type.
     *
     * @param type the class we want to check
     * @return true if the class represents a valid property type
     */
    public static boolean isPropertyType(final Type type) {
        if (type instanceof ParameterizedType)
            return isPropertyType(((ParameterizedType) type).getRawType());
        if (type instanceof Class)
            return isPropertyType((Class) type);

        throw new RuntimeException("bad type, not parameterized...");
    }

    public static boolean isPropertyType(final Class type) {
        if (type == null) {
            return false;
        }

        return isPrimitiveLike(type) || (type == DBRef.class) || (type == Pattern.class) ||
                (type == CodeWScope.class) || (type == ObjectId.class) || (type == Key.class) ||
                (type == DBObject.class) || (type == BasicDBObject.class) || (type == BasicDBList.class);
    }

    public static boolean isPrimitiveLike(final Class type) {
        if (type == null) {
            return false;
        }

        return (type == String.class) || (type == char.class) || (type == Character.class) || (type == short.class)
                || (type == Short.class) || (type == Integer.class) || (type == int.class) || (type == Long.class)
                || (type == long.class) || (type == Double.class) || (type == double.class) || (type == float.class)
                || (type == Float.class) || (type == Boolean.class) || (type == boolean.class) || (type == Byte.class)
                || (type == byte.class) || (type == Date.class) || (type == Locale.class) || (type == Class.class)
                || (type == UUID.class) || (type == URI.class) || type.isEnum();
    }

    /**
     * Get the (first) class that parameterizes the Field supplied.
     *
     * @param field the field
     * @return the class that parameterizes the field, or null if field is not
     *         parameterized
     */
    public static Class getParameterizedClass(final Field field) {
        return getParameterizedClass(field, 0);
    }

    /**
     * Get the class that parameterizes the Field supplied, at the index
     * supplied (field can be parameterized with multiple param classes).
     *
     * @param field the field
     * @param index the index of the parameterizing class
     * @return the class that parameterizes the field, or null if field is not
     *         parameterized
     */
    public static Class getParameterizedClass(final Field field, final int index) {
        if (field.getGenericType() instanceof ParameterizedType) {
            ParameterizedType ptype = (ParameterizedType) field.getGenericType();
            if ((ptype.getActualTypeArguments() != null) && (ptype.getActualTypeArguments().length <= index)) {
                return null;
            }
            Type paramType = ptype.getActualTypeArguments()[index];
            if (paramType instanceof GenericArrayType) {
                Class arrayType = (Class) ((GenericArrayType) paramType).getGenericComponentType();
                return Array.newInstance(arrayType, 0).getClass();
            } else {
                if (paramType instanceof ParameterizedType) {
                    ParameterizedType paramPType = (ParameterizedType) paramType;
                    return (Class) paramPType.getRawType();
                } else {
                    if (paramType instanceof TypeVariable) {
                        // TODO: Figure out what to do... Walk back up the to
                        // the parent class and try to get the variable type
                        // from the T/V/X
                        throw new MappingException("Generic Typed Class not supported:  <" + ((TypeVariable) paramType).getName() + "> = "
                                + ((TypeVariable) paramType).getBounds()[0]);
                    } else if (paramType instanceof Class) {
                        return (Class) paramType;
                    } else {
                        throw new MappingException("Unknown type... pretty bad... call for help, wave your hands... yeah!");
                    }
                }
            }
        }
        return null;
    }

    public static Type getParameterizedType(final Field field, final int index) {
        if (field.getGenericType() instanceof ParameterizedType) {
            ParameterizedType ptype = (ParameterizedType) field.getGenericType();
            if ((ptype.getActualTypeArguments() != null) && (ptype.getActualTypeArguments().length <= index)) {
                return null;
            }
            Type paramType = ptype.getActualTypeArguments()[index];
            if (paramType instanceof GenericArrayType) {
                return ((GenericArrayType) paramType).getGenericComponentType();
            } else {
                if (paramType instanceof ParameterizedType) {
                    return paramType;
                } else {
                    if (paramType instanceof TypeVariable) {
                        // TODO: Figure out what to do... Walk back up the to
                        // the parent class and try to get the variable type
                        // from the T/V/X
//            throw new MappingException("Generic Typed Class not supported:  <" + ((TypeVariable) paramType).getName() + "> = " + ((TypeVariable) paramType).getBounds()[0]);
                        return paramType;
                    } else if (paramType instanceof Class) {
                        return (Class) paramType;
                    } else {
                        throw new MappingException("Unknown type... pretty bad... call for help, wave your hands... yeah!");
                    }
                }
            }
        }
        return null;
    }

    public static Class getTypeArgumentOfParameterizedClass(final Field field, final int index, final int typeIndex) {
        if (field.getGenericType() instanceof ParameterizedType) {
            ParameterizedType ptype = (ParameterizedType) field.getGenericType();
            Type paramType = ptype.getActualTypeArguments()[index];
            if (!(paramType instanceof GenericArrayType)) {
                if (paramType instanceof ParameterizedType) {
                    ParameterizedType paramPType = (ParameterizedType) paramType;
                    Type paramParamType = paramPType.getActualTypeArguments()[typeIndex];
                    if (!(paramParamType instanceof ParameterizedType)) {
                        return (Class) paramParamType;
                    }
                }
            }
        }
        return null;
    }

    public static Class getParameterizedClass(final Class c) {
        return getParameterizedClass(c, 0);
    }

    public static Class getParameterizedClass(final Class c, final int index) {
        TypeVariable[] typeVars = c.getTypeParameters();
        if (typeVars.length > 0) {
            TypeVariable typeVariable = typeVars[index];
            Type[] bounds = typeVariable.getBounds();

            Type type = bounds[0];
            if (type instanceof Class) {
                return (Class) type;// broke for enumset, cause bounds contain
                // type instead of class
            } else
                return null;
        } else {
            return null;
        }
    }

    /**
     * Check if a field is parameterized with a specific class.
     *
     * @param field the field
     * @param c     the class to check against
     * @return true if the field is parameterized and c is the class that
     *         parameterizes the field, or is an interface that the
     *         parameterized class implements, else false
     */
    public static boolean isFieldParameterizedWithClass(final Field field, final Class c) {
        if (field.getGenericType() instanceof ParameterizedType) {
            ParameterizedType ptype = (ParameterizedType) field.getGenericType();
            for (Type type : ptype.getActualTypeArguments()) {
                if (type == c) {
                    return true;
                }
                if (c.isInterface() && implementsInterface((Class) type, c)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Check if the field supplied is parameterized with a valid JCR property
     * type.
     *
     * @param field the field
     * @return true if the field is parameterized with a valid JCR property
     *         type, else false
     */
    public static boolean isFieldParameterizedWithPropertyType(final Field field) {
        if (field.getGenericType() instanceof ParameterizedType) {
            ParameterizedType ptype = (ParameterizedType) field.getGenericType();
            for (Type type : ptype.getActualTypeArguments()) {
                if (isPropertyType((Class) type)) {
                    return true;
                }
            }
        }
        return false;
    }

    public static <T> T getAnnotation(final Class c, final Class<T> annClass) {
        ArrayList<T> found = getAnnotations(c, annClass);
        if (found != null && found.size() > 0)
            return found.get(0);
        else
            return null;
    }

    /**
     * Returns a list of the found Annotations hierarchically.
     * <p/>
     * Returns the given class, it's super-classes (and their interfaces),
     * and finally the interfaces of the given class, in that order.
     *
     * @return An ArrayList of <T>.
     */
    public static <T> ArrayList<T> getAnnotations(final Class c, final Class<T> annClass) {
        ArrayList<T> found = new ArrayList<T>();
        // TODO isn't that actually breaking the contract of @Inherited?
        if (c.isAnnotationPresent(annClass)) {
            found.add((T) c.getAnnotation(annClass));
        }

        Class parent = c.getSuperclass();
        while ((parent != null) && (parent != Object.class)) {
            if (parent.isAnnotationPresent(annClass)) {
                found.add((T) parent.getAnnotation(annClass));
            }

            // ...and interfaces that the superclass implements
            for (Class interfaceClass : parent.getInterfaces()) {
                if (interfaceClass.isAnnotationPresent(annClass)) {
                    found.add((T) interfaceClass.getAnnotation(annClass));
                }
            }

            parent = parent.getSuperclass();
        }

        // ...and all implemented interfaces
        for (Class interfaceClass : c.getInterfaces()) {
            if (interfaceClass.isAnnotationPresent(annClass)) {
                found.add((T) interfaceClass.getAnnotation(annClass));
            }
        }
        return found;
    }

    public static Embedded getClassEmbeddedAnnotation(final Class c) {
        return getAnnotation(c, Embedded.class);
    }

    public static Entity getClassEntityAnnotation(final Class c) {
        return getAnnotation(c, Entity.class);
    }

    private static String stripFilenameExtension(final String filename) {
        if (filename.indexOf('.') != -1) {
            return filename.substring(0, filename.lastIndexOf('.'));
        } else {
            return filename;
        }
    }

    public static Set<Class<?>> getFromDirectory(final File directory, final String packageName)
            throws ClassNotFoundException {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        if (directory.exists()) {
            for (String file : directory.list()) {
                if (file.endsWith(".class")) {
                    String name = packageName + '.' + stripFilenameExtension(file);
                    Class<?> clazz = Class.forName(name);
                    classes.add(clazz);
                }
            }
        }
        return classes;
    }

    public static Set<Class<?>> getFromJARFile(final String jar, final String packageName) throws IOException,
            FileNotFoundException, ClassNotFoundException {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        JarInputStream jarFile = new JarInputStream(new FileInputStream(jar));
        JarEntry jarEntry;
        do {
            jarEntry = jarFile.getNextJarEntry();
            if (jarEntry != null) {
                String className = jarEntry.getName();
                if (className.endsWith(".class")) {
                    className = stripFilenameExtension(className);
                    if (className.startsWith(packageName)) {
                        classes.add(Class.forName(className.replace('/', '.')));
                    }
                }
            }
        }
        while (jarEntry != null);
        return classes;
    }

    public static Set<Class<?>> getClasses(final String packageName) throws IOException, ClassNotFoundException {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        return getClasses(loader, packageName);
    }

    public static Set<Class<?>> getClasses(final ClassLoader loader, final String packageName) throws IOException,
            ClassNotFoundException {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = loader.getResources(path);
        if (resources != null) {
            while (resources.hasMoreElements()) {
                String filePath = resources.nextElement().getFile();
                // WINDOWS HACK
                if (filePath.indexOf("%20") > 0)
                    filePath = filePath.replaceAll("%20", " ");
                // # in the jar name
                if (filePath.indexOf("%23") > 0)
                    filePath = filePath.replaceAll("%23", "#");

                if (filePath != null) {
                    if ((filePath.indexOf("!") > 0) & (filePath.indexOf(".jar") > 0)) {
                        String jarPath = filePath.substring(0, filePath.indexOf("!")).substring(
                                filePath.indexOf(":") + 1);
                        // WINDOWS HACK
                        if (jarPath.indexOf(":") >= 0) {
                            jarPath = jarPath.substring(1);
                        }
                        classes.addAll(getFromJARFile(jarPath, path));
                    } else {
                        classes.addAll(getFromDirectory(new File(filePath), packageName));
                    }
                }
            }
        }
        return classes;
    }

    /**
     * create a new instance of the entity, first using the dbObject field, then
     * by calling createInstence based on the type
     */
//  public static Object createInstance(final MappedField mf, final DBObject dbObject) {
//    // see if there is a className value
//    return createInstance(mf.getConcreteType(), dbObject);
//  }

//    public static Object createInstance(final Class type)
//    {
//        try
//        {
//            return getNoArgsConstructor(type).newInstance();
//        }
//        catch (Exception e)
//        {
//            throw new RuntimeException(e);
//        }
//    }

    /**
     * gets the Class for some classname, or if the className is not found,
     * return the defaultClass instance
     */
//  public static Class getClassForName(final String className, final Class defaultClass) {
//    try {
//      Class c = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
//      return c;
//    } catch (ClassNotFoundException ex) {
//      return defaultClass;
//    }
//  }

    /**
     * create a new instance of the entity, first using the dbObject field, then
     * by calling createInstence based on the type
     */
//  public static Object createInstance(final Class entityClass, final DBObject dbObject) {
//    // see if there is a className value
//    String className = (String) dbObject.get(Mapper.CLASS_NAME_FIELDNAME);
//    Class c = entityClass;
//    if (className != null) {
//      // try to Class.forName(className) as defined in the dbObject first,
//      // otherwise return the entityClass
//      c = getClassForName(className, entityClass);
//    }
//    return createInstance(c);
//  }

//  public static Object newInstance(final Class<?> c, final Class<?> fallbackType) {
//    return newInstance(getNoArgsConstructor(c), fallbackType);
//  }

//  private static Constructor getNoArgsConstructor(final Class ctorType) {
//    try {
//      Constructor ctor = ctorType.getDeclaredConstructor();
//      ctor.setAccessible(true);
//      return ctor;
//    } catch (NoSuchMethodException e) {
//      throw new MappingException("No usable constructor for " + ctorType.getName(), e);
//    }
//  }
    public static ArrayList iterToList(Iterable it) {
        if (it instanceof ArrayList) return (ArrayList) it;
        if (it == null) return null;

        ArrayList ar = new ArrayList();
        for (Object o : it)
            ar.add(o);

        return ar;
    }

    public static Object convertToArray(final Class type, final List<?> values) {
        Object exampleArray = Array.newInstance(type, values.size());
        try {
            Object[] array = values.toArray((Object[]) exampleArray);
            return array;
        } catch (ClassCastException e) {
            for (int i = 0; i < values.size(); i++) {
                Array.set(exampleArray, i, values.get(i));
            }
            return exampleArray;
        }
    }


    /**
     * Get the underlying class for a type, or null if the type is a variable
     * type.
     *
     * @param type the type
     * @return the underlying class
     */
    public 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;
        }
    }

    /**
     * Get the actual type arguments a child class has used to extend a generic
     * base class.
     *
     * @param baseClass  the base class
     * @param childClass the child class
     * @return a list of the raw classes for the actual type arguments.
     */
    public static <T> List<Class<?>> getTypeArguments(Class<T> baseClass, Class<? extends T> childClass) {
        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
        Type type = childClass;
        // start walking up the inheritance hierarchy until we hit baseClass
        while (!getClass(type).equals(baseClass)) {
            if (type instanceof Class) {
                // there is no useful information for us in raw types, so just
                // keep going.
                type = ((Class) type).getGenericSuperclass();
            } else {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Class<?> rawType = (Class) parameterizedType.getRawType();

                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
                for (int i = 0; i < actualTypeArguments.length; i++) {
                    resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
                }

                if (!rawType.equals(baseClass)) {
                    type = rawType.getGenericSuperclass();
                }
            }
        }

        // finally, for each actual type argument provided to baseClass,
        // determine (if possible)
        // the raw class for that type argument.
        Type[] actualTypeArguments;
        if (type instanceof Class) {
            actualTypeArguments = ((Class) type).getTypeParameters();
        } else {
            actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
        }
        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
        // resolve types by chasing down type variables.
        for (Type baseType : actualTypeArguments) {
            while (resolvedTypes.containsKey(baseType)) {
                baseType = resolvedTypes.get(baseType);
            }
            typeArgumentsAsClasses.add(getClass(baseType));
        }
        return typeArgumentsAsClasses;
    }

    public static <T> Class<?> getTypeArgument(Class<? extends T> clazz, TypeVariable<? extends GenericDeclaration> tv) {
        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
        Type type = clazz;
        // start walking up the inheritance hierarchy until we hit the end
        while (!getClass(type).equals(Object.class)) {
            if (type instanceof Class) {
                // there is no useful information for us in raw types, so just
                // keep going.
                type = ((Class) type).getGenericSuperclass();
            } else {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Class<?> rawType = (Class) parameterizedType.getRawType();

                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
                for (int i = 0; i < actualTypeArguments.length; i++) {
                    if (typeParameters[i].equals(tv)) {
                        Class cls = getClass(actualTypeArguments[i]);
                        if (cls != null)
                            return cls;
                        return getClass(resolvedTypes.get(actualTypeArguments[i]));
                    }
                    resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
                }

                if (!rawType.equals(Object.class)) {
                    type = rawType.getGenericSuperclass();
                }
            }
        }

        return null;
    }
}
TOP

Related Classes of com.github.jmkgreen.morphia.utils.ReflectionUtils

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.