blob: 6a8185dc14fb7acb94484a08d316d8a1d6d4f5bf [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2010 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.utility.internal;
/**
* Convenience methods related to Java class names as returned by
* {@link java.lang.Class#getName()}.
*/
public class ClassName {
public static final String VOID_CLASS_NAME = ReflectionTools.VOID_CLASS.getName();
public static final String VOID_WRAPPER_CLASS_NAME = ReflectionTools.VOID_WRAPPER_CLASS.getName();
public static final char REFERENCE_CLASS_CODE = 'L';
public static final char REFERENCE_CLASS_NAME_DELIMITER = ';';
/**
* Return whether the specified class is an array type.
* @see java.lang.Class#getName()
*/
public static boolean isArray(String className) {
return className.charAt(0) == '[';
}
/**
* Return the "element type" of the specified class.
* The element type is the base type held by an array type.
* Non-array types simply return themselves.
* @see java.lang.Class#getName()
*/
public static String getElementTypeName(String className) {
int depth = getArrayDepth(className);
if (depth == 0) {
// the name is in the form: "java.lang.Object" or "int"
return className;
}
return getElementTypeName_(className, depth);
}
/**
* pre-condition: array depth is not zero
*/
private static String getElementTypeName_(String className, int arrayDepth) {
int last = className.length() - 1;
if (className.charAt(arrayDepth) == REFERENCE_CLASS_CODE) {
// the name is in the form: "[[[Ljava.lang.Object;"
return className.substring(arrayDepth + 1, last); // drop the trailing ';'
}
// the name is in the form: "[[[I"
return forCode(className.charAt(last));
}
/**
* Return the "array depth" of the specified class.
* The depth is the number of dimensions for an array type.
* Non-array types have a depth of zero.
* @see java.lang.Class#getName()
*/
public static int getArrayDepth(String className) {
int depth = 0;
while (className.charAt(depth) == '[') {
depth++;
}
return depth;
}
/**
* Return the specified class's component type.
* Return <code>null</code> if the specified class is not an array type.
* @see java.lang.Class#getName()
*/
public static String getComponentTypeName(String className) {
switch (getArrayDepth(className)) {
case 0:
return null;
case 1:
return getElementTypeName_(className, 1);
default:
return className.substring(1);
}
}
/**
* Return the specified class's simple name.
* Return an empty string if the specified class is anonymous.
* <p>
* The simple name of an array type is the simple name of the
* component type with <code>"[]"</code> appended. In particular,
* the simple name of an array type whose component type is
* anonymous is simply <code>"[]"</code>.
* @see java.lang.Class#getSimpleName()
*/
public static String getSimpleName(String className) {
return isArray(className) ?
getSimpleName(getComponentTypeName(className)) + "[]" : //$NON-NLS-1$
getSimpleName_(className);
}
/**
* pre-condition: specified class is not an array type
*/
private static String getSimpleName_(String className) {
int index = className.lastIndexOf('$');
if (index == -1) { // "top-level" class - strip package name
return className.substring(className.lastIndexOf('.') + 1);
}
int len = className.length();
for (int i = ++index; i < len; i++) {
if ( ! charIsAsciiDigit(className.charAt(i))) {
return className.substring(i); // "member" or "local" class
}
}
// all the characters past the '$' are ASCII digits ("anonymous" class)
return ""; //$NON-NLS-1$
}
/**
* Return the specified class's package name (e.g.
* <code>"java.lang.Object"</code> returns
* <code>"java.lang"</code>).
* Return an empty string if the specified class is:<ul>
* <li>in the "default" package
* <li>an array class
* <li>a primtive class
* </ul>
* @see java.lang.Class#getPackage()
* @see java.lang.Package#getName()
*/
public static String getPackageName(String className) {
if (isArray(className)) {
return ""; //$NON-NLS-1$
}
int lastPeriod = className.lastIndexOf('.');
return (lastPeriod == -1) ? "" : className.substring(0, lastPeriod); //$NON-NLS-1$
}
/**
* Return whether the specified class is a "top-level" class,
* as opposed to a "member", "local", or "anonymous" class,
* using the standard JDK naming conventions (i.e. the class
* name does NOT contain a <code>'$'</code>).
* A "top-level" class can be either the "primary" (public) class defined
* in a file/compilation unit (i.e. the class with the same name as its
* file's simple base name) or a "non-primary" (package visible) class
* (i.e. the other top-level classes defined in a file).
* A "top-level" class can contain any of the other types of classes.
* @see java.lang.Class#getName()
*/
public static boolean isTopLevel(String className) {
if (isArray(className)) {
return false;
}
return className.lastIndexOf('$') == -1;
}
/**
* Return whether the specified class is a "member" class,
* as opposed to a "top-level", "local", or "anonymous" class,
* using the standard JDK naming convention (i.e. the class
* name ends with a <code>'$'</code> followed by a legal class name; e.g.
* <code>"TopLevelClass$1LocalClass$MemberClass"</code>).
* A "member" class can be either "nested" (static) or "inner";
* but there is no way to determine which from the class's name.
* A "member" class can contain "local", "anonymous", or other
* "member" classes; and vice-versa.
* @see java.lang.Class#getName()
*/
public static boolean isMember(String className) {
if (isArray(className)) {
return false;
}
int index = className.lastIndexOf('$');
if (index == -1) {
return false; // "top-level" class
}
// the character immediately after the dollar sign cannot be an ASCII digit
return ! charIsAsciiDigit(className.charAt(++index));
}
/**
* Return whether the specified class is a "local" class,
* as opposed to a "top-level", "member", or "anonymous" class,
* using the standard JDK naming convention (i.e. the class name
* ends with <code>"$nnnXXX"</code>,
* where the <code>'$'</code> is
* followed by a series of numeric digits which are followed by the
* local class name; e.g. <code>"TopLevelClass$1LocalClass"</code>).
* A "local" class can contain "member", "anonymous", or other
* "local" classes; and vice-versa.
* @see java.lang.Class#getName()
*/
public static boolean isLocal(String className) {
if (isArray(className)) {
return false;
}
int index = className.lastIndexOf('$');
if (index == -1) {
return false; // "top-level" class
}
if ( ! charIsAsciiDigit(className.charAt(++index))) {
return false; // "member" class
}
int len = className.length();
for (int i = ++index; i < len; i++) {
if ( ! charIsAsciiDigit(className.charAt(i))) {
return true;
}
}
// all the characters past the '$' are ASCII digits ("anonymous" class)
return false;
}
/**
* Return whether the specified class is an "anonymous" class,
* as opposed to a "top-level", "member", or "local" class,
* using the standard JDK naming convention (i.e. the class
* name ends with <code>"$nnn"</code> where all the characters past the
* last <code>'$'</code> are ASCII numeric digits;
* e.g. <code>"TopLevelClass$1"</code>).
* An "anonymous" class can contain "member", "local", or other
* "anonymous" classes; and vice-versa.
* @see java.lang.Class#getName()
*/
public static boolean isAnonymous(String className) {
if (isArray(className)) {
return false;
}
int index = className.lastIndexOf('$');
if (index == -1) {
return false; // "top-level" class
}
if ( ! charIsAsciiDigit(className.charAt(++index))) {
return false; // "member" class
}
int len = className.length();
for (int i = ++index; i < len; i++) {
if ( ! charIsAsciiDigit(className.charAt(i))) {
return false; // "local" class
}
}
// all the characters past the '$' are ASCII digits ("anonymous" class)
return true;
}
/**
* {@link Character#isDigit(char)} returns <code>true</code> for some non-ASCII
* digits. This method does not.
*/
private static boolean charIsAsciiDigit(char c) {
return ('0' <= c) && (c <= '9');
}
/**
* Return whether the specified class is a "reference"
* class (i.e. neither <code>void</code> nor one of the primitive variable classes,
* <code>boolean</code>, <code>int</code>, <code>float</code>, etc.).
* <p>
* <strong>NB:</strong> <code>void.class.isPrimitive() == true</code>
*/
public static boolean isReference(String className) {
return ! isPrimitive(className);
}
/**
* Return whether the specified class is a primitive
* class (i.e. <code>void</code> or one of the primitive variable classes,
* <code>boolean</code>, <code>int</code>, <code>float</code>, etc.).
* <p>
* <strong>NB:</strong> <code>void.class.isPrimitive() == true</code>
*/
public static boolean isPrimitive(String className) {
if (isArray(className) || (className.length() > ReflectionTools.MAX_PRIMITIVE_CLASS_NAME_LENGTH)) {
return false; // performance tweak
}
for (ReflectionTools.Primitive primitive : ReflectionTools.PRIMITIVES) {
if (className.equals(primitive.javaClass.getName())) {
return true;
}
}
return false;
}
/**
* Return whether the specified class is a primitive wrapper
* class (i.e. <code>java.lang.Void</code> or one of the primitive
* variable wrapper classes, <code>java.lang.Boolean</code>,
* <code>java.lang.Integer</code>, <code>java.lang.Float</code>, etc.).
* <p>
* <strong>NB:</strong> <code>void.class.isPrimitive() == true</code>
*/
public static boolean isPrimitiveWrapper(String className) {
if (isArray(className) || (className.length() > ReflectionTools.MAX_PRIMITIVE_WRAPPER_CLASS_NAME_LENGTH)) {
return false; // performance tweak
}
for (ReflectionTools.Primitive primitive : ReflectionTools.PRIMITIVES) {
if (className.equals(primitive.wrapperClass.getName())) {
return true;
}
}
return false;
}
/**
* Return whether the specified class is a "variable" primitive
* class (i.e. <code>boolean</code>, <code>int</code>, <code>float</code>, etc.,
* but not <code>void</code>).
* <p>
* <strong>NB:</strong> <code>void.class.isPrimitive() == true</code>
*/
public static boolean isVariablePrimitive(String className) {
return isPrimitive(className)
&& ( ! className.equals(VOID_CLASS_NAME));
}
/**
* Return whether the specified class is a "variable" primitive wrapper
* class (i.e. <code>java.lang.Boolean</code>,
* <code>java.lang.Integer</code>, <code>java.lang.Float</code>, etc.,
* but not <code>java.lang.Void</code>).
* <p>
* <strong>NB:</strong> <code>void.class.isPrimitive() == true</code>
*/
public static boolean isVariablePrimitiveWrapper(String className) {
return isPrimitiveWrapper(className)
&& ( ! className.equals(VOID_WRAPPER_CLASS_NAME));
}
/**
* Return the name of the primitive wrapper class corresponding to the specified
* primitive class. Return <code>null</code> if the specified class is not a primitive.
*/
public static String getWrapperClassName(String primitiveClassName) {
for (ReflectionTools.Primitive primitive : ReflectionTools.PRIMITIVES) {
if (primitive.javaClass.getName().equals(primitiveClassName)) {
return primitive.wrapperClass.getName();
}
}
return null;
}
/**
* Return the name of the primitive class corresponding to the specified
* primitive wrapper class. Return <code>null</code> if the specified class
* is not a primitive wrapper.
*/
public static String getPrimitiveClassName(String primitiveWrapperClassName) {
for (ReflectionTools.Primitive primitive : ReflectionTools.PRIMITIVES) {
if (primitive.wrapperClass.getName().equals(primitiveWrapperClassName)) {
return primitive.javaClass.getName();
}
}
return null;
}
// ********** primitive codes **********
/**
* Return the primitive class name for the specified primitive class code.
* Return <code>null</code> if the specified code
* is not a primitive class code.
* @see java.lang.Class#getName()
*/
public static String forCode(int classCode) {
return forCode((char) classCode);
}
/**
* Return the primitive class name for the specified primitive class code.
* Return <code>null</code> if the specified code
* is not a primitive class code.
* @see java.lang.Class#getName()
*/
public static String forCode(char classCode) {
Class<?> primitiveClass = ReflectionTools.getClassForCode(classCode);
return (primitiveClass == null) ? null : primitiveClass.getName();
}
/**
* Return the class code for the specified primitive class.
* Return <code>0</code> if the specified class
* is not a primitive class.
* @see java.lang.Class#getName()
*/
public static char getCodeForClassName(String primitiveClassName) {
if (( ! isArray(primitiveClassName)) && (primitiveClassName.length() <= ReflectionTools.MAX_PRIMITIVE_CLASS_NAME_LENGTH)) {
for (ReflectionTools.Primitive primitive : ReflectionTools.PRIMITIVES) {
if (primitive.javaClass.getName().equals(primitiveClassName)) {
return primitive.code;
}
}
}
return 0;
}
static void append(String className, StringBuilder sb) {
sb.append(REFERENCE_CLASS_CODE);
sb.append(className);
sb.append(REFERENCE_CLASS_NAME_DELIMITER);
}
// ********** suppressed constructor **********
/**
* Suppress default constructor, ensuring non-instantiability.
*/
private ClassName() {
super();
throw new UnsupportedOperationException();
}
}