Package org.apache.geronimo.kernel

Source Code of org.apache.geronimo.kernel.ClassLoading

/**
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You 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 org.apache.geronimo.kernel;

import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.Set;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;

import org.apache.geronimo.kernel.config.MultiParentClassLoader;

/**
* Utility class for loading classes by a variety of name variations.
* <p/>
* Supported names types are:
* <p/>
* 1)  Fully qualified class name (e.g., "java.lang.String", "org.apache.geronimo.kernel.ClassLoading"
* 2)  Method signature encoding ("Ljava.lang.String;", "J", "I", etc.)
* 3)  Primitive type names ("int", "boolean", etc.)
* 4)  Method array signature strings ("[I", "[Ljava.lang.String")
* 5)  Arrays using Java code format ("int[]", "java.lang.String[][]")
* <p/>
* The classes are loaded using the provided class loader.  For the basic types, the primitive
* reflection types are returned.
*
* @version $Rev: 983503 $
*/
public class ClassLoading {

    /**
     * Table for mapping primitive class names/signatures to the implementing
     * class object
     * initialCapacity is calculated from 12 * 0.75 = 9
     */
    private static final HashMap<String, Class<?>> HUMAN_READABLE_PRIMITIVE_CLASS_MAP = new HashMap<String, Class<?>>(12);

    private static final HashMap<String, Class<?>> BINARY_NAME_PRIMITIVE_CLASS_MAP = new HashMap<String, Class<?>>(12);

    /**
     * Table for mapping primitive classes back to their name signature type, which
     * allows a reverse mapping to be performed from a class object into a resolvable
     * signature.
     */
    private static final HashMap<Class<?>, String> CLASS_TO_SIGNATURE_MAP = new HashMap<Class<?>, String>(12);


    /**
     * Setup the primitives map.  We make any entry for each primitive class using both the
     * human readable name and the method signature shorthand type.
     */
    static {
        HUMAN_READABLE_PRIMITIVE_CLASS_MAP.put("boolean", boolean.class);
        BINARY_NAME_PRIMITIVE_CLASS_MAP.put("Z", boolean.class);
        HUMAN_READABLE_PRIMITIVE_CLASS_MAP.put("byte", byte.class);
        BINARY_NAME_PRIMITIVE_CLASS_MAP.put("B", byte.class);
        HUMAN_READABLE_PRIMITIVE_CLASS_MAP.put("char", char.class);
        BINARY_NAME_PRIMITIVE_CLASS_MAP.put("C", char.class);
        HUMAN_READABLE_PRIMITIVE_CLASS_MAP.put("short", short.class);
        BINARY_NAME_PRIMITIVE_CLASS_MAP.put("S", short.class);
        HUMAN_READABLE_PRIMITIVE_CLASS_MAP.put("int", int.class);
        BINARY_NAME_PRIMITIVE_CLASS_MAP.put("I", int.class);
        HUMAN_READABLE_PRIMITIVE_CLASS_MAP.put("long", long.class);
        BINARY_NAME_PRIMITIVE_CLASS_MAP.put("J", long.class);
        HUMAN_READABLE_PRIMITIVE_CLASS_MAP.put("float", float.class);
        BINARY_NAME_PRIMITIVE_CLASS_MAP.put("F", float.class);
        HUMAN_READABLE_PRIMITIVE_CLASS_MAP.put("double", double.class);
        BINARY_NAME_PRIMITIVE_CLASS_MAP.put("D", double.class);
        HUMAN_READABLE_PRIMITIVE_CLASS_MAP.put("void", void.class);
        BINARY_NAME_PRIMITIVE_CLASS_MAP.put("V", void.class);

        // Now build a reverse mapping table.  The table above has a many-to-one mapping for
        // class names.  To do the reverse, we need to pick just one.  As long as the
        // returned name supports "round tripping" of the requests, this will work fine.

        CLASS_TO_SIGNATURE_MAP.put(boolean.class, "Z");
        CLASS_TO_SIGNATURE_MAP.put(byte.class, "B");
        CLASS_TO_SIGNATURE_MAP.put(char.class, "C");
        CLASS_TO_SIGNATURE_MAP.put(short.class, "S");
        CLASS_TO_SIGNATURE_MAP.put(int.class, "I");
        CLASS_TO_SIGNATURE_MAP.put(long.class, "J");
        CLASS_TO_SIGNATURE_MAP.put(float.class, "F");
        CLASS_TO_SIGNATURE_MAP.put(double.class, "D");
        CLASS_TO_SIGNATURE_MAP.put(void.class, "V");
    }


    /**
     * Load a class that matches the requested name, using the provided class loader context.
     * <p/>
     * The class name may be a standard class name, the name of a primitive type Java
     * reflection class (e.g., "boolean" or "int"), or a type in method type signature
     * encoding.  Array classes in either encoding form are also processed.
     *
     * @param className The name of the required class.
     * @param classLoader The class loader used to resolve the class object.
     * @return The Class object resolved from "className".
     * @throws ClassNotFoundException When unable to resolve the class object.
     * @throws IllegalArgumentException If either argument is null.
     */
    public static Class loadClass(String className, ClassLoader classLoader) throws ClassNotFoundException {

        // the tests require IllegalArgumentExceptions for null values on either of these.
        if (className == null) {
            throw new IllegalArgumentException("className is null");
        }

        if (classLoader == null) {
            throw new IllegalArgumentException("classLoader is null");
        }

        //First check the human readable primitive class from cached map
        Class<?> resolvedClass;
        if (className.length() <= 7) {
            resolvedClass = HUMAN_READABLE_PRIMITIVE_CLASS_MAP.get(className);
            if (resolvedClass != null) {
                return resolvedClass;
            }
        }

        // The easiest case is a proper class name.  We just have the class loader resolve this.
        // If the class loader throws a ClassNotFoundException, then we need to check each of the
        // special name encodings we support.
        try {
            return classLoader.loadClass(className);
        } catch (ClassNotFoundException ignore) {
            // if not found, continue on to the other name forms.
        }


        // The second easiest version to resolve is a direct map to a primitive type name
        // or method signature.  Check our name-to-class map for one of those.
        resolvedClass = BINARY_NAME_PRIMITIVE_CLASS_MAP.get(className);
        if (resolvedClass != null) {
            return resolvedClass;
        }

        // Class names in method signature have the format "Lfully.resolved.name;",
        // so if it ends in a semicolon and begins with an "L", this must be in
        // this format.  Have the class loader try to load this.  There are no other
        // options if this fails, so just allow the class loader to throw the
        // ClassNotFoundException.
        if (className.endsWith(";") && className.startsWith("L")) {
            // pick out the name portion
            String typeName = className.substring(1, className.length() - 1);
            // and delegate the loading to the class loader.
            return classLoader.loadClass(typeName);
        }

        // All we have left now are the array types.  Method signature array types
        // have a series of leading "[" characters to specify the number of dimensions.
        // The other array type we handle uses trailing "[]" for the dimensions, just
        // like the Java language syntax.

        // first check for the signature form ([[[[type).
        if (className.charAt(0) == '[') {
            // we have at least one array marker, now count how many leading '['s we have
            // to get the dimension count.
            int count = 0;
            int nameLen = className.length();

            while (count < nameLen && className.charAt(count) == '[') {
                count++;
            }

            // pull of the name subtype, which is everything after the last '['
            String arrayTypeName = className.substring(count, className.length());
            // resolve the type using a recursive call, which will load any of the primitive signature
            // types as well as class names.
            Class arrayType = loadClass(arrayTypeName, classLoader);

            // Resolving array types require a little more work.  The array classes are
            // created dynamically when the first instance of a given dimension and type is
            // created.  We need to create one using reflection to do this.
            return getArrayClass(arrayType, count);
        }


        // ok, last chance.  Now check for an array specification in Java language
        // syntax.  This will be a type name followed by pairs of "[]" to indicate
        // the number of dimensions.
        if (className.endsWith("[]")) {
            // get the base component class name and the arrayDimensions
            int count = 0;
            int position = className.length();

            while (position > 1 && className.substring(position - 2, position).equals("[]")) {
                // count this dimension
                count++;
                // and step back the probe position.
                position -= 2;
            }

            // position now points at the location of the last successful test.  This makes it
            // easy to pick off the class name.

            String typeName = className.substring(0, position);

            // load the base type, again, doing this recursively
            Class arrayType = loadClass(typeName, classLoader);
            // and turn this into the class object
            return getArrayClass(arrayType, count);
        }

        // We're out of options, just toss an exception over the wall.
        if (classLoader instanceof MultiParentClassLoader) {
            MultiParentClassLoader cl = (MultiParentClassLoader) classLoader;
            throw new ClassNotFoundException("Could not load class " + className + " from classloader: " + cl.getId() + ", destroyed state: " + cl.isDestroyed());
        }
        throw new ClassNotFoundException("Could not load class " + className + " from unknown classloader; " + classLoader);
    }


    /**
     * Map a class object back to a class name.  The returned class object
     * must be "round trippable", which means
     * <p/>
     * type == ClassLoading.loadClass(ClassLoading.getClassName(type), classLoader)
     * <p/>
     * must be true.  To ensure this, the class name is always returned in
     * method signature format.
     *
     * @param type The class object we convert into name form.
     * @return A string representation of the class name, in method signature
     *         format.
     */
    public static String getClassName(Class type) {
        StringBuffer name = new StringBuffer();

        // we test these in reverse order from the resolution steps,
        // first handling arrays, then primitive types, and finally
        // "normal" class objects.

        // First handle arrays.  If a class is an array, the type is
        // element stored at that level.  So, for a 2-dimensional array
        // of ints, the top-level type will be "[I".  We need to loop
        // down the hierarchy until we hit a non-array type.
        while (type.isArray()) {
            // add another array indicator at the front of the name,
            // and continue with the next type.
            name.append('[');
            type = type.getComponentType();
        }

        // we're down to the base type.  If this is a primitive, then
        // we poke in the single-character type specifier.
        if (type.isPrimitive()) {
            name.append(CLASS_TO_SIGNATURE_MAP.get(type));
        }
        // a "normal" class.  This gets expressing using the "Lmy.class.name;" syntax.
        else {
            name.append('L');
            name.append(type.getName());
            name.append(';');
        }
        return name.toString();
    }

    private static Class getArrayClass(Class type, int dimension) {
        // Array.newInstance() requires an array of the requested number of dimensions
        // that gives the size for each dimension.  We just request 0 in each of the
        // dimentions, which is not unlike a black hole sigularity.
        int dimensions[] = new int[dimension];
        // create an instance and return the associated class object.
        return Array.newInstance(type, dimensions).getClass();
    }

    public static Set getAllTypes(Class type) {
        Set allTypes = new LinkedHashSet();
        allTypes.add(type);
        allTypes.addAll(getAllSuperClasses(type));
        allTypes.addAll(getAllInterfaces(type));
        return allTypes;
    }

    private static Set getAllSuperClasses(Class clazz) {
        Set allSuperClasses = new LinkedHashSet();
        for (Class superClass = clazz.getSuperclass(); superClass != null; superClass = superClass.getSuperclass()) {
            allSuperClasses.add(superClass);
        }
        return allSuperClasses;
    }

    private static Set getAllInterfaces(Class clazz) {
        Set allInterfaces = new LinkedHashSet();
        LinkedList stack = new LinkedList();
        stack.addAll(Arrays.asList(clazz.getInterfaces()));
        while (!stack.isEmpty()) {
            Class intf = (Class) stack.removeFirst();
            if (!allInterfaces.contains(intf)) {
                allInterfaces.add(intf);
                stack.addAll(Arrays.asList(intf.getInterfaces()));
            }
        }
        return allInterfaces;
    }

    public static Set reduceInterfaces(Set source) {
        Class[] classes = (Class[]) source.toArray(new Class[source.size()]);
        classes = reduceInterfaces(classes);
        return new LinkedHashSet(Arrays.asList(classes));
    }

    /**
     * If there are multiple interfaces, and some of them extend each other,
     * eliminate the superclass in favor of the subclasses that extend them.
     *
     * If one of the entries is a class (not an interface), make sure it's
     * the first one in the array.  If more than one of the entries is a
     * class, throws an IllegalArgumentException
     *
     * @param source the original list of interfaces
     * @return the equal or smaller list of interfaces
     */
    public static Class[] reduceInterfaces(Class[] source) {
        // use a copy of the sorce array
        source = source.clone();

        for (int leftIndex = 0; leftIndex < source.length-1; leftIndex++) {
            Class left = source[leftIndex];
            if(left == null) {
                continue;
            }

            for (int rightIndex = leftIndex +1; rightIndex < source.length; rightIndex++) {
                Class right = source[rightIndex];
                if(right == null) {
                    continue;
                }

                if(left == right || right.isAssignableFrom(left)) {
                    // right is the same as class or a sub class of left
                    source[rightIndex] = null;
                } else if(left.isAssignableFrom(right)) {
                    // left is the same as class or a sub class of right
                    source[leftIndex] = null;

                    // the left has been eliminated; move on to the next left
                    break;
                }
            }
        }

        Class clazz = null;
        for (int i = 0; i < source.length; i++) {
            if (source[i] != null && !source[i].isInterface()) {
                if (clazz != null) {
                    throw new IllegalArgumentException("Source contains two classes which are not subclasses of each other: " + clazz.getName() + ", " + source[i].getName());
                }
                clazz = source[i];
                source[i] = null;
            }
        }

        List list = new ArrayList(source.length);
        if (clazz != null) list.add(clazz);
        for (int i = 0; i < source.length; i++) {
            if(source[i] != null) {
                list.add(source[i]);
            }
        }
        return (Class[]) list.toArray(new Class[list.size()]);
    }
}
TOP

Related Classes of org.apache.geronimo.kernel.ClassLoading

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.