Package org.jpox.util

Source Code of org.jpox.util.ClassUtils

/**********************************************************************
Copyright (c) 2004 Andy Jefferson and others. All rights reserved.
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.


Contributors:
    ...
**********************************************************************/
package org.jpox.util;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.jpox.ClassLoaderResolver;
import org.jpox.ClassNameConstants;
import org.jpox.ObjectManagerFactoryImpl;
import org.jpox.TypeManager;
import org.jpox.exceptions.JPOXException;

/**
* Utilities for handling classes.
* These are to supplement the methods provided by the Class object.
*
* @version $Revision: 1.56 $
**/
public class ClassUtils
{
    /** Localisation utility for output messages */
    protected static final Localiser LOCALISER = Localiser.getInstance("org.jpox.Localisation",
        ObjectManagerFactoryImpl.class.getClassLoader());

    /** caching for constructors - using caching, the perf is at least doubled **/
    protected static Map constructorsCache = new SoftValueMap();

    /**
     * Accessor for a new instance of an object.
     * Uses reflection to generate the instance using the passed constructor parameter arguments.
     * @param type Type of object (the class).
     * @param parameterTypes Classes of params for the constructor
     * @param parameters The parameters for the constructor
     * @return The object
     * @throws JPOXException If an error occurs creating the instance
     */
    public static Object newInstance(Class type, Class[] parameterTypes, Object[] parameters)
    {
        Object obj;
        try
        {
            String name = ""+type.hashCode();
            if (parameterTypes != null)
            {
                for (int i=0;i<parameterTypes.length; i++)
                {
                    name += "-"+parameterTypes[i].hashCode();
                }
            }
            Constructor ctor = (Constructor)constructorsCache.get(name);
            if (ctor ==null)
            {
                ctor = type.getConstructor(parameterTypes);
                constructorsCache.put(name, ctor);
            }
            obj = ctor.newInstance(parameters);
        }
        catch (NoSuchMethodException e)
        {
            throw new JPOXException(LOCALISER.msg("030004", type.getName(),
                Arrays.asList(parameterTypes).toString()+" "+Arrays.asList(type.getConstructors()).toString()), new Exception[]{e}).setFatal();
        }
        catch (IllegalAccessException e)
        {
            throw new JPOXException(LOCALISER.msg("030005", type.getName()), new Exception[]{e}).setFatal();
        }
        catch (InstantiationException e)
        {
            throw new JPOXException(LOCALISER.msg("030006", type.getName()), new Exception[]{e}).setFatal();
        }
        catch (InvocationTargetException e)
        {
            Throwable t = e.getTargetException();
            if (t instanceof RuntimeException)
            {
                throw (RuntimeException) t;
            }
            else if (t instanceof Error)
            {
                throw (Error) t;
            }
            else
            {
                throw new JPOXException(LOCALISER.msg("030007", type.getName(), t)).setFatal();
            }
        }
        return obj;
    }

    /**
     * Convenience method to return the constructor of the passed class that accepts the supplied
     * argument types. Allows for primitive to primitive wrapper conversion. Typically used by
     * the JDOQL ResultClass mapping process.
     * @param cls The class
     * @param types The constructor argument types
     * @return The constructor
     */
    public static Constructor getConstructorWithArguments(Class cls, Class[] types)
    {
        try
        {
            Constructor[] constructors = cls.getConstructors();
            if (constructors != null)
            {
                // Check the types of the constructor and find one that matches allowing for
                // primitive to wrapper conversion
                for (int i=0;i<constructors.length;i++)
                {
                    Class[] ctrParams = constructors[i].getParameterTypes();
                    boolean ctrIsValid = true;

                    // Discard any constructor with a different number of params
                    if (ctrParams != null && ctrParams.length == types.length)
                    {
                        for (int j=0;j<ctrParams.length;j++)
                        {
                            // Compare the type with the object or any primitive
                            Class primType = ClassUtils.getPrimitiveTypeForType(types[j]);
                            if (types[j] == null && ctrParams[j].isPrimitive())
                            {
                                // Null type for field so has to accept nulls, and primitives don't do that
                                ctrIsValid = false;
                                break;
                            }
                            else if (types[j] != null && ctrParams[j] != types[j] &&
                                (primType == null ||
                                 primType != null && ctrParams[j] != primType))
                            {
                                // Different type in this parameter position
                                ctrIsValid = false;
                                break;
                            }
                        }
                    }
                    else
                    {
                        ctrIsValid = false;
                    }

                    if (ctrIsValid)
                    {
                        return constructors[i];
                    }
                }
            }
        }
        catch (SecurityException se)
        {
            // Can't access the constructors
        }

        return null;
    }

    /**
     * Obtain a method from a class or superclasses using reflection.
     * The method will have the specified name and will take a single argument.
     * Allows for the argument type being primitive or its associated wrapper.
     * @param cls the class to find the declared fields and populate the map
     * @param methodName the method name to find
     * @param argType the argument type
     */
    public static Method getMethodWithArgument(Class cls, String methodName, Class argType)
    {
        Method m = ClassUtils.getMethodForClass(cls, methodName, new Class[] {argType});
        if (m == null)
        {
            Class primitive = ClassUtils.getPrimitiveTypeForType(argType);
            if (primitive != null)
            {
                m = ClassUtils.getMethodForClass(cls, methodName, new Class[] {primitive});
            }
        }
        return m;
    }

    /**
     * Obtain a method from a class or superclasses using reflection
     * @param cls the class where to look for the method
     * @param methodName the method name to find
     * @param argtypes the classes argument of the method
     * @return The Method
     */
    public static Method getMethodForClass(Class cls, String methodName, Class[] argtypes)
    {
        try
        {
            return cls.getDeclaredMethod(methodName, argtypes);
        }
        catch (NoSuchMethodException e)
        {
            if (cls.getSuperclass() != null)
            {
                return ClassUtils.getMethodForClass(cls.getSuperclass(), methodName, argtypes);
            }
        }
        catch (Exception e)
        {
            // do nothing
        }
        return null;
    }

    /**
     * Utility to perform the same function as the JDK 1.4 method
     * Boolean.valueOf(boolean), so we have no real dependency on JDK 1.4.
     * Please remove when our minimum requirement is JDK1.4 or above.
     * @param value The value
     * @return The Boolean value
     */
    public static Boolean booleanValueOf(boolean value)
    {
        if (value)
        {
            return Boolean.TRUE;
        }
        else
        {
            return Boolean.FALSE;
        }
    }

    /**
     * Utility to get the Class name given the file name and the file name of
     * the root of the package structure.
     * @param filename The filename of the class file
     * @param rootfilename The filename of the root of the package structure
     * @return The Class name
     **/
    public static String getClassnameForFilename(String filename,
                                                 String rootfilename)
    {
        if (filename == null)
        {
            return null;
        }

        String classname=filename;

        // Omit the root part of the filename
        if (rootfilename != null)
        {
            classname = classname.substring(rootfilename.length());
        }

        // Remove the ".class" suffix
        classname = classname.substring(0,classname.length()-6);

        String file_separator=System.getProperty("file.separator");

        // Remove any leading separator
        if (classname.indexOf(file_separator) == 0)
        {
            classname = classname.substring(file_separator.length());
        }

        // Change all separator characters to "."
        classname = StringUtils.replaceAll(classname,file_separator,".");

        return classname;
    }

    /**
     * Method to return the class files below the specified directory.
     * @param dir The directory
     * @param normal_classes Whether to include normal classes
     * @param inner_classes Whether to include inner classes
     * @return The class files (Collection of File objects).
     **/
    public static Collection getClassFilesForDirectory(File dir,
                                        boolean normal_classes,
                                        boolean inner_classes)
    {
        if (dir == null)
        {
            return null;
        }

        Collection classes=new HashSet();
        File[] files=dir.listFiles();
        if (files != null)
        {
            for (int i=0;i<files.length;i++)
            {
                if (files[i].isFile())
                {
                    // If this is a class file, add it
                    if (files[i].getName().endsWith(".class"))
                    {
                        boolean is_inner_class=isInnerClass(files[i].getName());
                        if ((normal_classes && !is_inner_class) ||
                            (inner_classes && is_inner_class))
                        {
                            classes.add(files[i]);
                        }
                    }
                }
                else
                {
                    // Check for classes in subdirectories
                    Collection child_classes=getClassFilesForDirectory(files[i],normal_classes,inner_classes);
                    if (child_classes != null && child_classes.size() > 0)
                    {
                        classes.addAll(child_classes);
                    }
                }
            }
        }

        return classes;
    }

    /**
     * Convenience accessor for the names of all class files in the jar file with the specified name.
     * The returned class names are of the form "org.jpox.MyClass".
     * @param jarFileName Name of the jar file
     * @return The class names
     */
    public static String[] getClassNamesForJarFile(String jarFileName)
    {
        try
        {
            JarFile jar = new JarFile(jarFileName);
            return getClassNamesForJarFile(jar);
        }
        catch (IOException ioe)
        {
            JPOXLogger.GENERAL.warn("Error opening the jar file " + jarFileName + " : " + ioe.getMessage());
        }
        return null;
    }

    /**
     * Convenience accessor for the names of all class files in the jar file with the specified URL.
     * The returned class names are of the form "org.jpox.MyClass".
     * @param jarFileURL URL for the jar file
     * @return The class names
     */
    public static String[] getClassNamesForJarFile(URL jarFileURL)
    {
        File jarFile = new File(jarFileURL.getFile()); // TODO Check for errors
        try
        {
            JarFile jar = new JarFile(jarFile);
            return getClassNamesForJarFile(jar);
        }
        catch (IOException ioe)
        {
            JPOXLogger.GENERAL.warn("Error opening the jar file " + jarFileURL.getFile() + " : " + ioe.getMessage());
        }
        return null;
    }

    /**
     * Convenience method to return the names of classes specified in the jar file.
     * All inner classes are ignored.
     * @param jar Jar file
     * @return The class names
     */
    private static String[] getClassNamesForJarFile(JarFile jar)
    {
        Enumeration jarEntries = jar.entries();
        HashSet classes = new HashSet();
        String file_separator = System.getProperty("file.separator");
        while (jarEntries.hasMoreElements())
        {
            String entry = ((JarEntry)jarEntries.nextElement()).getName();
            if (entry.endsWith(".class") && !ClassUtils.isInnerClass(entry))
            {
                String className = entry.substring(0, entry.length()-6); // Omit ".class"
                className = StringUtils.replaceAll(className, file_separator, ".");
                classes.add(className);
            }
        }
        return (String[])classes.toArray(new String[classes.size()]);
    }

    /**
     * Convenience accessor for the names of all "package.jdo" files in the jar file with the specified name.
     * @param jarFileName Name of the jar file
     * @return The "package.jdo" file names
     */
    public static String[] getPackageJdoFilesForJarFile(String jarFileName)
    {
        try
        {
            JarFile jar = new JarFile(jarFileName);
            return getFileNamesWithSuffixForJarFile(jar, "package.jdo");
        }
        catch (IOException ioe)
        {
            JPOXLogger.GENERAL.warn("Error opening the jar file " + jarFileName + " : " + ioe.getMessage());
        }
        return null;
    }

    /**
     * Convenience accessor for the names of all "package.jdo" files in the jar file with the specified URL.
     * @param jarFileURL URL for the jar file
     * @return The "package.jdo" file names
     */
    public static String[] getPackageJdoFilesForJarFile(URL jarFileURL)
    {
        File jarFile = new File(jarFileURL.getFile()); // TODO Check for errors
        try
        {
            JarFile jar = new JarFile(jarFile);
            return getFileNamesWithSuffixForJarFile(jar, "package.jdo");
        }
        catch (IOException ioe)
        {
            JPOXLogger.GENERAL.warn("Error opening the jar file " + jarFileURL.getFile() + " : " + ioe.getMessage());
        }
        return null;
    }

    /**
     * Convenience method to return the names of files specified in the jar file that end with
     * the specified suffix.
     * @param jar Jar file
     * @param suffix Suffix for the file (can be the filename without the path)
     * @return The fully-qualified names of the files with this suffix in the jar file
     */
    private static String[] getFileNamesWithSuffixForJarFile(JarFile jar, String suffix)
    {
        Enumeration jarEntries = jar.entries();
        HashSet files = new HashSet();
        while (jarEntries.hasMoreElements())
        {
            String entry = ((JarEntry)jarEntries.nextElement()).getName();
            if (entry.endsWith(suffix))
            {
                files.add(entry);
            }
        }
        return (String[])files.toArray(new String[files.size()]);
    }

    /**
     * Method to check whether a classname is for an inner class.
     * Currently checks for the presence of $ in the name.
     * @param class_name The class name
     * @return Whether it is an inner class
     **/
    public static boolean isInnerClass(String class_name)
    {
        if (class_name == null)
        {
            return false;
        }
        else if (class_name.indexOf('$') >= 0)
        {
            return true;
        }
        return false;
    }

    /**
     * Method to check for a default constructor on a class.
     * Particular relevance for JDO is the requirement for a default
     * constructor on all Persistence-Capable classes. Doesn't check
     * superclasses for the default constructor.
     * @param the_class The class
     * @return Whether it has a default constructor
     **/
    public static boolean hasDefaultConstructor(Class the_class)
    {
        if (the_class == null)
        {
            return false;
        }
        try
        {
            the_class.getDeclaredConstructor(null);
        }
        catch (Exception e)
        {
            return false;
        }

        return true;
    }

    /**
     * Method to return the superclasses for a class.
     * The superclasses will be ordered.
     * @param the_class The class
     * @return The superclass of this class.
     **/
    public static Collection getSuperclasses(Class the_class)
    {
        List superclasses = new ArrayList();

        Class c = the_class;
        boolean more_superclasses = true;
        while (more_superclasses)
        {
            Class superclass = c.getSuperclass();
            if (superclass != null)
            {
                superclasses.add(superclass);
                c = superclass;
            }
            else
            {
                more_superclasses = false;
            }
        }
        return superclasses;
    }

    /**
     * Method to return the superinterfaces for a class.
     * The superinterfaces will be ordered.
     * @param the_class The class
     * @return The superinterfaces of this class.
     **/
    public static Collection getSuperinterfaces(Class the_class)
    {
        List superintfs = new ArrayList();

        Class c = the_class;
        Class[] superinterfaces = c.getInterfaces();
        if (superinterfaces != null)
        {
            for (int i=0; i<superinterfaces.length; i++)
            {
                if (!superintfs.contains(superinterfaces[i]))
                {
                    superintfs.add(superinterfaces[i]);
                    superintfs.addAll(ClassUtils.getSuperinterfaces(superinterfaces[i]));
                }
            }
        }
        return superintfs;
    }

    /**
     * Obtain a field from a class or superclasses using reflection.
     * @param cls the class to find the field from
     * @param fieldName the field name to find
     * @return The Field
     */
    public static Field getFieldForClass(Class cls, String fieldName)
    {
        try
        {
            return cls.getDeclaredField(fieldName);
        }
        catch (NoSuchFieldException e)
        {
            if (cls.getSuperclass() != null)
            {
                return ClassUtils.getFieldForClass(cls.getSuperclass(), fieldName);
            }
        }
        catch (Exception e)
        {
            // do nothing
        }
        return null;
    }

    /**
     * Convenience method to return the object wrapper type for a primitive type name.
     * If the type is not a primitive then just returns the type name
     * @param typeName The primitive type name
     * @return The object wrapper type name for this primitive
     */
    public static String getWrapperTypeNameForPrimitiveTypeName(String typeName)
    {
        if (typeName.equals("boolean"))
        {
            return ClassNameConstants.JAVA_LANG_BOOLEAN;
        }
        else if (typeName.equals("byte"))
        {
            return ClassNameConstants.JAVA_LANG_BYTE;
        }
        else if (typeName.equals("char"))
        {
            return ClassNameConstants.JAVA_LANG_CHARACTER;
        }
        else if (typeName.equals("double"))
        {
            return ClassNameConstants.JAVA_LANG_DOUBLE;
        }
        else if (typeName.equals("float"))
        {
            return ClassNameConstants.JAVA_LANG_FLOAT;
        }
        else if (typeName.equals("int"))
        {
            return ClassNameConstants.JAVA_LANG_INTEGER;
        }
        else if (typeName.equals("long"))
        {
            return ClassNameConstants.JAVA_LANG_LONG;
        }
        else if (typeName.equals("short"))
        {
            return ClassNameConstants.JAVA_LANG_SHORT;
        }
        else
        {
            return typeName;
        }
    }

    /**
     * Convenience method to return the object wrapper type for a primitive type.
     * @param type The primitive type
     * @return The object wrapper type for this primitive
     */
    public static Class getWrapperTypeForPrimitiveType(Class type)
    {
        if (type == boolean.class)
        {
            return Boolean.class;
        }
        else if (type == byte.class)
        {
            return Byte.class;
        }
        else if (type == char.class)
        {
            return Character.class;
        }
        else if (type == double.class)
        {
            return Double.class;
        }
        else if (type == float.class)
        {
            return Float.class;
        }
        else if (type == int.class)
        {
            return Integer.class;
        }
        else if (type == long.class)
        {
            return Long.class;
        }
        else if (type == short.class)
        {
            return Short.class;
        }
        return null;
    }

    /**
     * Method to return the primitive equivalent of the specified type (if any).
     * Returns null if there is no primitive equivalent.
     * @param type The type
     * @return The primitive equivalent.
     */
    public static Class getPrimitiveTypeForType(Class type)
    {
        if (type == Boolean.class)
        {
            return boolean.class;
        }
        else if (type == Byte.class)
        {
            return byte.class;
        }
        else if (type == Character.class)
        {
            return char.class;
        }
        else if (type == Double.class)
        {
            return double.class;
        }
        else if (type == Float.class)
        {
            return float.class;
        }
        else if (type == Integer.class)
        {
            return int.class;
        }
        else if (type == Long.class)
        {
            return long.class;
        }
        else if (type == Short.class)
        {
            return short.class;
        }
        else
        {
            return null;
        }
    }

    /**
     * Convenience method to return if the passed type (name) is a primitive wrapper type.
     * @param typeName Name of the type
     * @return Whether it is a primitive wrapper
     */
    public static boolean isPrimitiveWrapperType(String typeName)
    {
        if (typeName.equals(ClassNameConstants.JAVA_LANG_BOOLEAN) ||
            typeName.equals(ClassNameConstants.JAVA_LANG_BYTE) ||
            typeName.equals(ClassNameConstants.JAVA_LANG_CHARACTER) ||
            typeName.equals(ClassNameConstants.JAVA_LANG_DOUBLE) ||
            typeName.equals(ClassNameConstants.JAVA_LANG_FLOAT) ||
            typeName.equals(ClassNameConstants.JAVA_LANG_INTEGER) ||
            typeName.equals(ClassNameConstants.JAVA_LANG_LONG) ||
            typeName.equals(ClassNameConstants.JAVA_LANG_SHORT))
        {
            return true;
        }
        return false;
    }

    /**
     * Convenience method to return if the passed type (name) is a primitive array type.
     * @param typeName Name of the type
     * @return Whether it is a primitive array
     */
    public static boolean isPrimitiveArrayType(String typeName)
    {
        if (typeName.equals(ClassNameConstants.BOOLEAN_ARRAY) ||
            typeName.equals(ClassNameConstants.BYTE_ARRAY) ||
            typeName.equals(ClassNameConstants.CHAR_ARRAY) ||
            typeName.equals(ClassNameConstants.DOUBLE_ARRAY) ||
            typeName.equals(ClassNameConstants.FLOAT_ARRAY) ||
            typeName.equals(ClassNameConstants.INT_ARRAY) ||
            typeName.equals(ClassNameConstants.LONG_ARRAY) ||
            typeName.equals(ClassNameConstants.SHORT_ARRAY))
        {
            return true;
        }
        return false;
    }

    /**
     * Convenience method to return if the passed type (name) is a primitive type.
     * @param typeName Name of the type
     * @return Whether it is a primitive
     */
    public static boolean isPrimitiveType(String typeName)
    {
        if (typeName.equals(ClassNameConstants.BOOLEAN) ||
            typeName.equals(ClassNameConstants.BYTE) ||
            typeName.equals(ClassNameConstants.CHAR) ||
            typeName.equals(ClassNameConstants.DOUBLE) ||
            typeName.equals(ClassNameConstants.FLOAT) ||
            typeName.equals(ClassNameConstants.INT) ||
            typeName.equals(ClassNameConstants.LONG) ||
            typeName.equals(ClassNameConstants.SHORT))
        {
            return true;
        }
        return false;
    }

    /**
     * Convenience method to convert the passed value to an object of the specified type (if possible).
     * If no such conversion is supported will return null. If the required type is a primitive will
     * return an object of the wrapper type.
     * @param value The value
     * @param cls The class
     * @return The converted value (or null)
     */
    public static Object convertValue(Object value, Class cls)
    {
        if (value == null)
        {
            return null;
        }

        Class type = cls;
        if (cls.isPrimitive())
        {
            type = getWrapperTypeForPrimitiveType(cls);
        }
        if (type.isAssignableFrom(value.getClass()))
        {
            // Already in the correct type
            return value;
        }

        if (type == Long.class && value instanceof Number)
        {
            return new Long(((Number)value).longValue());
        }
        else if (type == Integer.class && value instanceof Number)
        {
            return new Integer(((Number)value).intValue());
        }
        else if (type == Short.class && value instanceof Number)
        {
            return new Short(((Number)value).shortValue());
        }
        else if (type == Float.class && value instanceof Number)
        {
            return new Float(((Number)value).doubleValue());
        }
        else if (type == Double.class && value instanceof Number)
        {
            return new Double(((Number)value).doubleValue());
        }
        return null;
    }

    /**
     * Convenience method to return if two types are compatible.
     * Returns true if both types are primitive/wrappers and are of the same type.
     * Returns true if clsName2 is the same or a subclass of cls1.
     * Otherwise returns false;
     * @param cls1 First class
     * @param clsName2 Name of the second class
     * @param clr ClassLoader resolver to use
     * @return Whether they are compatible
     */
    public static boolean typesAreCompatible(Class cls1, String clsName2, ClassLoaderResolver clr)
    {
        if (clr.isAssignableFrom(cls1, clsName2))
        {
            return true;
        }

        // Cater for primitive and primitive wrappers being compatible
        if (cls1.isPrimitive())
        {
            return clr.isAssignableFrom(ClassUtils.getWrapperTypeForPrimitiveType(cls1), clsName2);
        }
        else if (ClassUtils.isPrimitiveWrapperType(cls1.getName()))
        {
            return clr.isAssignableFrom(ClassUtils.getPrimitiveTypeForType(cls1), clsName2);
        }

        return false;
    }

    /**
     * Convenience method to return if two types are compatible.
     * Returns true if both types are primitive/wrappers and are of the same type.
     * Returns true if cls2 is the same or a subclass of cls1.
     * Otherwise returns false;
     * @param cls1 First class
     * @param cls2 Second class
     * @return Whether they are compatible
     */
    public static boolean typesAreCompatible(Class cls1, Class cls2)
    {
        if (cls1.isAssignableFrom(cls2))
        {
            return true;
        }

        // Cater for primitive and primitive wrappers being compatible
        if (cls1.isPrimitive())
        {
            return ClassUtils.getWrapperTypeForPrimitiveType(cls1).isAssignableFrom(cls2);
        }

        return false;
    }

    /**
     * Utility to create the full class name given the package and class name.
     * Some examples
     * <PRE>
     * packageName=test className=Test, returns result=test.Test
     * packageName=test className=test1.Test, returns result=test1.Test
     * packageName=&lt;null&gt; className=Test, returns result=Test
     * packageName=&lt;null&gt; className=test1.Test, returns result=test1.Test
     * </PRE>
     * @param pkg_name package name.
     * @param cls_name class name.
     * @return generated full class name.
     */
    public static String createFullClassName(String pkg_name, String cls_name)
    {
        if (StringUtils.isWhitespace(cls_name))
        {
            throw new IllegalArgumentException("Class name not specified");
        }
        else if (StringUtils.isWhitespace(pkg_name))
        {
            return cls_name;
        }
        else if (cls_name.indexOf('.') >= 0)
        {
            return cls_name;
        }
        return pkg_name + "." + cls_name;
    }

    /**
     * Convenience method to return the passed type as a java.lang type wherever possible.
     * The passed type will be stripped of any package name and will be checked if it is
     * a known java.lang class. This is used where the user has specified a class name
     * for a collection or map element/key/value type and meant a java.lang class but didn't
     * fully qualify it.
     * @param type The type name
     * @return The java.lang equivalent (or the input type if not possible)
     */
    public static String getJavaLangClassForType(String type)
    {
        // Strip off any package name
        String baseType = null;
        if (type.lastIndexOf('.') < 0)
        {
            baseType = type;
        }
        else
        {
            baseType = type.substring(type.lastIndexOf('.')+1);
        }

        // Check against our known (supported) java.lang classes
        if (baseType.equals("String") || baseType.equals("Object") || baseType.equals("Boolean") || baseType.equals("Byte") ||
            baseType.equals("Character") || baseType.equals("Double") || baseType.equals("Float") || baseType.equals("Integer") ||
            baseType.equals("Long") || baseType.equals("Short") || baseType.equals("Number") || baseType.equals("StringBuffer"))
        {
            return "java.lang." + baseType;
        }
        return type;
    }

    /**
     * Method to check if 2 classes are direct descendents. So one of them is a
     * superclass of the other.
     * @param clr ClassLoaderResolver for loading the classes
     * @param class_name_1 Name of first class
     * @param class_name_2 Name of second class
     * @return Whether they are direct descendents.
     */
    public static boolean classesAreDescendents(ClassLoaderResolver clr,
                                                String class_name_1,
                                                String class_name_2)
    {
        Class class_1=clr.classForName(class_name_1);
        Class class_2=clr.classForName(class_name_2);
        if (class_1 == null || class_2 == null)
        {
            return false;
        }

        // Check for direct descendents
        if (class_1.isAssignableFrom(class_2) ||
            class_2.isAssignableFrom(class_1))
        {
            return true;
        }

        return false;
    }

    /**
     * Utility to use Reflection to dump out the details of a class.
     * Will list all superclasses, interfaces, methods and fields.
     * Can be used, for example, in checking the methods adding by the
     * enhancement process. The information is dumped out the JPOX GENERAL log.
     * @param cls The class to dump out to the log
     */
    public static void dumpClassInformation(Class cls)
    {
        JPOXLogger.GENERAL.info("----------------------------------------");
        JPOXLogger.GENERAL.info("Class Information for class " + cls.getName());

        // Superclasses
        Collection superclasses=ClassUtils.getSuperclasses(cls);
        Iterator superclass_iter=superclasses.iterator();
        while (superclass_iter.hasNext())
        {
            Class superclass=(Class)superclass_iter.next();
            JPOXLogger.GENERAL.info("    Superclass : " + superclass.getName());
        }

        // Interfaces
        Class[] interfaces=cls.getInterfaces();
        if (interfaces != null)
        {
            for (int i=0;i<interfaces.length;i++)
            {
                JPOXLogger.GENERAL.info("    Interface : " + interfaces[i].getName());
            }
        }

        // Methods
        try
        {
            Method[] methods=cls.getDeclaredMethods();
            for (int i=0;i<methods.length;i++)
            {
                JPOXLogger.GENERAL.info("    Method : " + methods[i].toString());
            }
        }
        catch (Exception e)
        {
        }

        // Fields
        try
        {
            Field[] fields=cls.getDeclaredFields();
            for (int i=0;i<fields.length;i++)
            {
                JPOXLogger.GENERAL.info("    Field : " + fields[i].toString());
            }
        }
        catch (Exception e)
        {
        }
        JPOXLogger.GENERAL.info("----------------------------------------");
    }
   
    /**
     * Generate a JavaBeans compatible getter name
     * @param fieldName the field name
     * @param isBoolean whether the field is primitive boolean type
     * @return the getter name
     */
    public static String getJavaBeanGetterName(String fieldName, boolean isBoolean)
    {
        if (fieldName == null)
        {
            return null;
        }
        String prefix = isBoolean ? "is" : "get";
        String name = fieldName.toUpperCase().charAt(0) + fieldName.substring(1);
        return prefix + name;
    }

    /**
     * Generate a JavaBeans compatible setter name
     * @param fieldName the field name
     * @return the setter name
     */
    public static String getJavaBeanSetterName(String fieldName)
    {
        if (fieldName == null)
        {
            return null;
        }
        String prefix = "set";
        String name = fieldName.toUpperCase().charAt(0) + fieldName.substring(1);
        return prefix + name;
    }

    /**
     * Generate a field name for JavaBeans compatible getter method
     * @param methodName the method name
     * @return the field name
     */
    public static String getFieldNameForJavaBeanGetter(String methodName)
    {
        if (methodName == null)
        {
            return null;
        }
        if (methodName.startsWith("get"))
        {
            if (methodName.length() < 4)
            {
                return null;
            }
            else if (methodName.length() == 4)
            {
                return methodName.toLowerCase().substring(3);
            }
            else
            {
                if (Character.isUpperCase(methodName.charAt(3)) && Character.isUpperCase(methodName.charAt(4)))
                {
                    // Capitalised name (e.g URL) so dont lowercase first character
                    return methodName.substring(3);
                }
                else
                {
                    return methodName.toLowerCase().charAt(3) + methodName.substring(4);
                }
            }
        }
        else if (methodName.startsWith("is"))
        {
            if (methodName.length() < 3)
            {
                return null;
            }
            else if (methodName.length() == 3)
            {
                return methodName.toLowerCase().substring(2);
            }
            else
            {
                if (Character.isUpperCase(methodName.charAt(2)) && Character.isUpperCase(methodName.charAt(3)))
                {
                    // Capitalised name (e.g URL) so dont lowercase first character
                    return methodName.substring(2);
                }
                else
                {
                    return methodName.toLowerCase().charAt(2) + methodName.substring(3);
                }
            }
        }
        return null;
    }

    /**
     * Generate a field name for JavaBeans compatible setter method
     * @param methodName the method name
     * @return the field name
     */
    public static String getFieldNameForJavaBeanSetter(String methodName)
    {
        if (methodName == null)
        {
            return null;
        }
        if (methodName.startsWith("set"))
        {
            if (methodName.length() < 4)
            {
                return null;
            }
            else if (methodName.length() == 4)
            {
                return methodName.toLowerCase().substring(3);
            }
            else
            {
                if (Character.isUpperCase(methodName.charAt(3)) && Character.isUpperCase(methodName.charAt(4)))
                {
                    // Capitalised name (e.g URL) so dont lowercase first character
                    return methodName.substring(3);
                }
                else
                {
                    return methodName.toLowerCase().charAt(3) + methodName.substring(4);
                }
            }
        }
        return null;
    }

    /**
     * Utility to find the class name of a class given the absolute file name of its class file.
     * Creates a loader and loads the class directly to find it.
     * @param fileURL URL for the class file
     * @return The name of the class
     * @throws ClassNotFoundException Thrown when the file is not found
     */
    public static String getClassNameForFileURL(final URL fileURL)
    throws ClassNotFoundException
    {
        ClassLoader loader = (ClassLoader) AccessController.doPrivileged(
            new PrivilegedAction()
            {
                public Object run()
                {       
                    return new ClassLoader()
                    {
                        protected Class findClass(String name) throws ClassNotFoundException
                        {
                            // Always load the file
                            InputStream in = null;
                            try
                            {
                                in = new BufferedInputStream(fileURL.openStream());
                                ByteArrayOutputStream byteStr = new ByteArrayOutputStream();
                                int byt = -1;
                                while ((byt = in.read()) != -1)
                                {
                                    byteStr.write(byt);
                                }
                                byte byteArr[] = byteStr.toByteArray();
                                return defineClass(null, byteArr, 0, byteArr.length);
                            }
                            catch (final RuntimeException rex)
                            {
                                throw rex;
                            }
                            catch (final Exception ex)
                            {
                                ex.printStackTrace();
                                throw new ClassNotFoundException(name);
                            }
                            finally
                            {
                                if (in != null)
                                {
                                    try
                                    {
                                        in.close();
                                    }
                                    catch (final IOException ioe)
                                    {
                                        ioe.printStackTrace();
                                    }
                                }
                            }
                        }
                    };
                }
            });
        Class cls = loader.loadClass("garbage"); // The passed in name is not of relevance
        return (cls != null ? cls.getName() : null);
    }

    /**
     * Utility to return the package name for a class.
     * Allows for the result of class.getPackage() being null.
     * @param cls The class
     * @return The name of its package (or null if no package e.g a primitive)
     */
    public static String getPackageNameForClass(Class cls)
    {
        // Check getPackage and use that if specified.
        if (cls.getPackage() != null)
        {
            return cls.getPackage().getName();
        }
        int separator = cls.getName().lastIndexOf('.');
        if (separator < 0)
        {
            return null;
        }
        return cls.getName().substring(0, separator);
    }

    /**
     * Utility to return the class name without the package name for a class.
     * @param cls The class
     * @return The name of the class without its package
     */
    public static String getClassNameForClass(Class cls)
    {
        // Just strip off all up to the last separator since Class.getPackage is unreliable
        int separator = cls.getName().lastIndexOf('.');
        if (separator < 0)
        {
            return cls.getName();
        }
        return cls.getName().substring(separator+1);
    }

    /**
     * Convenience method to filter out any supported classes from a list.
     * @param typeMgr TypeManager defining the types supported
     * @param classNames Names of the classes
     * @return Names of the classes (omitting supported types)
     */
    public static String[] getUnsupportedClassNames(TypeManager typeMgr, String[] classNames)
    {
        // Filter out any "simple" type classes
        int filteredClasses = 0;
        for (int i = 0; i < classNames.length; ++i)
        {
            if (typeMgr.isSupportedType(classNames[i]))
            {
                classNames[i] = null;
                ++filteredClasses;
            }
        }
        if (filteredClasses == 0)
        {
            return classNames;
        }
        String[] restClasses = new String[classNames.length - filteredClasses];
        int m = 0;
        for (int i = 0; i < classNames.length; ++i)
        {
            if (classNames[i] != null)
            {
                restClasses[m++] = classNames[i];
            }
        }
        return restClasses;
    }

    /**
     * Convenience method to extract the element type of a collection when using JDK1.5 generics given the
     * input field.
     * @param field The field
     * @return The name of the element class
     */
    public static String getCollectionElementType(Field field)
    {
        return getCollectionElementType(field.getType(), field.getGenericType());
    }

    /**
     * Convenience method to extract the element type of a collection when using JDK1.5 generics given the
     * input field.
     * @param type the field type
     * @param genericType the generic type
     * @return The name of the element class
     */
    public static String getCollectionElementType(Class type, Type genericType)
    {
        if (!Collection.class.isAssignableFrom(type))
        {
            return null;
        }

        String elementType = null;
        if (genericType instanceof ParameterizedType)
        {
            ParameterizedType paramtype = (ParameterizedType)genericType;
            if (paramtype.getActualTypeArguments().length == 1)
            {
                if (paramtype.getActualTypeArguments()[0] instanceof Class)
                {
                    elementType = ((Class)paramtype.getActualTypeArguments()[0]).getName();
                }
            }
        }
        return elementType;
    }
   
    /**
     * Convenience method to extract the element type of a collection when using JDK1.5 generics, given
     * the input method (getter).
     * @param method The method
     * @return The name of the element class
     */
    public static String getCollectionElementType(Method method)
    {
        if (!Collection.class.isAssignableFrom(method.getReturnType()))
        {
            return null;
        }

        String elementType = null;
        if (method.getGenericReturnType() instanceof ParameterizedType)
        {
            ParameterizedType paramtype = (ParameterizedType)method.getGenericReturnType();
            if (paramtype.getActualTypeArguments().length == 1)
            {
                if (paramtype.getActualTypeArguments()[0] instanceof Class)
                {
                    elementType = ((Class)paramtype.getActualTypeArguments()[0]).getName();
                }
            }
        }
        return elementType;
    }

    /**
     * Convenience method to extract the key type of a map when using JDK1.5 generics given the
     * input field.
     * @param field The field
     * @return The name of the key class
     */
    public static String getMapKeyType(Field field)
    {
        return getMapKeyType(field.getType(),field.getGenericType());
    }
   
    /**
     * Convenience method to extract the key type of a map when using JDK1.5 generics given the
     * input field.
     * @param type the field type
     * @param genericType the generic type
     * @return The name of the key class
     */
    public static String getMapKeyType(Class type, Type genericType)
    {
        if (!Map.class.isAssignableFrom(type))
        {
            return null;
        }

        String keyType = null;
        if (genericType instanceof ParameterizedType)
        {
            ParameterizedType paramtype = (ParameterizedType)genericType;
            if (paramtype.getActualTypeArguments().length == 2)
            {
                if (paramtype.getActualTypeArguments()[0] instanceof Class)
                {
                    keyType = ((Class)paramtype.getActualTypeArguments()[0]).getName();
                }
            }
        }
        return keyType;
    }   

    /**
     * Convenience method to extract the key type of a map when using JDK1.5 generics given the
     * input method.
     * @param method The method
     * @return The name of the key class
     */
    public static String getMapKeyType(Method method)
    {
        if (!Map.class.isAssignableFrom(method.getReturnType()))
        {
            return null;
        }

        String keyType = null;
        if (method.getGenericReturnType() instanceof ParameterizedType)
        {
            ParameterizedType paramtype = (ParameterizedType)method.getGenericReturnType();
            if (paramtype.getActualTypeArguments().length == 2)
            {
                if (paramtype.getActualTypeArguments()[0] instanceof Class)
                {
                    keyType = ((Class)paramtype.getActualTypeArguments()[0]).getName();
                }
            }
        }
        return keyType;
    }

    /**
     * Convenience method to extract the value type of a map when using JDK1.5 generics given the
     * input field
     * @param field The field
     * @return The name of the value class
     */
    public static String getMapValueType(Field field)
    {
        return getMapValueType(field.getType(),field.getGenericType());
    }

    /**
     * Convenience method to extract the value type of a map when using JDK1.5 generics given the
     * input field
     * @param type the field type
     * @param genericType the generic type
     * @return The name of the value class
     */
    public static String getMapValueType(Class type, Type genericType)
    {
        if (!Map.class.isAssignableFrom(type))
        {
            return null;
        }

        String valueType = null;
        if (genericType instanceof ParameterizedType)
        {
            ParameterizedType paramtype = (ParameterizedType)genericType;
            if (paramtype.getActualTypeArguments().length == 2)
            {
                if (paramtype.getActualTypeArguments()[1] instanceof Class)
                {
                    valueType = ((Class)paramtype.getActualTypeArguments()[1]).getName();
                }
            }
        }
        return valueType;
    }
   
    /**
     * Convenience method to extract the value type of a map when using JDK1.5 generics given the
     * input method.
     * @param method The method
     * @return The name of the value class
     */
    public static String getMapValueType(Method method)
    {
        if (!Map.class.isAssignableFrom(method.getReturnType()))
        {
            return null;
        }

        String valueType = null;
        if (method.getGenericReturnType() instanceof ParameterizedType)
        {
            ParameterizedType paramtype = (ParameterizedType)method.getGenericReturnType();
            if (paramtype.getActualTypeArguments().length == 2)
            {
                if (paramtype.getActualTypeArguments()[1] instanceof Class)
                {
                    valueType = ((Class)paramtype.getActualTypeArguments()[1]).getName();
                }
            }
        }
        return valueType;
    }

    /**
     * Convenience accessor for the modifiers of a field in a class.
     * @param clr ClassLoader resolver
     * @param className Name of the class
     * @param fieldName Name of the field
     * @return The modifiers
     */
    public static int getModifiersForFieldOfClass(ClassLoaderResolver clr, String className, String fieldName)
    {
        try
        {
            Class cls = clr.classForName(className);
            Field fld = cls.getDeclaredField(fieldName);
            return fld.getModifiers();
        }
        catch (Exception e)
        {
            // Class or field not found
        }
        return -1;
    }
}
TOP

Related Classes of org.jpox.util.ClassUtils

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.