001/*
002 *  Copyright 2010, 2011 Christopher Pheby
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.jadira.bindings.core.utils.reflection;
017
018import java.util.HashMap;
019import java.util.Map;
020
021import org.jadira.bindings.core.utils.string.StringUtils;
022
023/**
024 * Holds utility methods for obtaining a class from a symbolic representation
025 * The result of {@link #determineQualifiedName(String)} is equivalent in format to the result of {@link Class#getName()}
026 */
027public final class ClassUtils {
028
029    public static final char PACKAGE_SEPARATOR_CHARACTER = '.';
030    public static final char INNER_CLASS_SEPARATOR_CHAR = '$';
031
032    private static final Map<String, String> PRIMITIVE_MAPPING = new HashMap<String, String>();
033
034    static {
035        PRIMITIVE_MAPPING.put("int", "I");
036        PRIMITIVE_MAPPING.put("boolean", "Z");
037        PRIMITIVE_MAPPING.put("float", "F");
038        PRIMITIVE_MAPPING.put("long", "J");
039        PRIMITIVE_MAPPING.put("short", "S");
040        PRIMITIVE_MAPPING.put("byte", "B");
041        PRIMITIVE_MAPPING.put("double", "D");
042        PRIMITIVE_MAPPING.put("char", "C");
043    }
044
045    private ClassUtils() {
046    }
047
048    /**
049     * Attempt to load the class matching the given qualified name.
050     * Uses the current Thread's class loader, or if unavailable, the classloader
051     * this class was loaded from.
052     * @param qualifiedName The qualified name to use
053     * @throws IllegalArgumentException If the class cannot be loaded
054     * @return The {@link Class} representing the given class
055     */
056    public static Class<?> getClass(String qualifiedName) {
057        return getClass(ClassLoaderUtils.getClassLoader(), qualifiedName);
058    }
059    
060    /**
061     * Attempt to load the class matching the given qualified name
062     * @param classLoader Classloader to use
063     * @param className The classname to use
064     * @throws IllegalArgumentException If the class cannot be loaded
065     * @return The {@link Class} representing the given class
066     */
067    public static Class<?> getClass(ClassLoader classLoader, String className) {
068        try {
069
070            final Class<?> clazz;
071
072            if (PRIMITIVE_MAPPING.containsKey(className)) {
073
074                String qualifiedName = "[" + PRIMITIVE_MAPPING.get(className);
075                clazz = Class.forName(qualifiedName, true, classLoader).getComponentType();
076            } else {
077                clazz = Class.forName(determineQualifiedName(className), true, classLoader);
078            }
079            return clazz;
080        } catch (ClassNotFoundException ex) {
081
082            int lastSeparatorIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHARACTER);
083
084            if (lastSeparatorIndex != -1) {
085                return getClass(classLoader, className.substring(0, lastSeparatorIndex) + INNER_CLASS_SEPARATOR_CHAR
086                        + className.substring(lastSeparatorIndex + 1));
087            }
088            throw new IllegalArgumentException("Unable to unmarshall String to Class: " + className);
089        }
090    }
091
092    /**
093     * Given a readable class name determine the JVM Qualified Name
094     * @param className The name to convert
095     * @return The JVM Qualified representation
096     */
097    public static String determineQualifiedName(String className) {
098
099        String readableClassName = StringUtils.removeWhitespace(className);
100        if (readableClassName == null) {
101
102            throw new IllegalArgumentException("readableClassName must not be null.");
103
104        } else if (readableClassName.endsWith("[]")) {
105
106            StringBuilder classNameBuffer = new StringBuilder();
107
108            while (readableClassName.endsWith("[]")) {
109                readableClassName = readableClassName.substring(0, readableClassName.length() - 2);
110                classNameBuffer.append("[");
111            }
112
113            String abbreviation = PRIMITIVE_MAPPING.get(readableClassName);
114
115            if (abbreviation == null) {
116                classNameBuffer.append("L").append(readableClassName).append(";");
117            } else {
118                classNameBuffer.append(abbreviation);
119            }
120
121            readableClassName = classNameBuffer.toString();
122        }
123        return readableClassName;
124    }
125    
126    /**
127     * Given a JVM Qualified Name produce a readable classname
128     * @param qualifiedName The Qualified Name
129     * @return The readable classname
130     */
131    public static String determineReadableClassName(String qualifiedName) {
132
133        String readableClassName = StringUtils.removeWhitespace(qualifiedName);
134        if (readableClassName == null) {
135
136            throw new IllegalArgumentException("qualifiedName must not be null.");
137        } else if (readableClassName.startsWith("[")) {
138
139            StringBuilder classNameBuffer = new StringBuilder();
140            while (readableClassName.startsWith("[")) {
141                classNameBuffer.append("[]");
142                readableClassName = readableClassName.substring(1);
143            }
144            
145            if (PRIMITIVE_MAPPING.containsValue(readableClassName)) {
146                
147                for (Map.Entry<String,String> next : PRIMITIVE_MAPPING.entrySet()) {
148                    if (next.getValue().equals(readableClassName)) {
149                        readableClassName = next.getKey() + classNameBuffer.toString();
150                        break;
151                    }
152                }
153                
154            } else if (readableClassName.startsWith("L") && readableClassName.endsWith(";")) {
155                readableClassName = readableClassName.substring(1, readableClassName.length() - 1) + classNameBuffer.toString();
156            } else {
157                throw new IllegalArgumentException("qualifiedName was invalid {" + readableClassName + "}");                
158            }
159        } else if (readableClassName.endsWith("]")) {
160            throw new IllegalArgumentException("qualifiedName was invalid {" + readableClassName + "}");
161        }
162        
163        return readableClassName;
164    }
165}