/******************************************************************************* * Copyright (c) 2009, 2017 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.equinox.p2.internal.repository.comparator.java; import java.util.ArrayList; /* * Provides methods for encoding and decoding type and method signature strings. *

* Signatures obtained from parsing source files (i.e. files with one of the * {@link JavaCore#getJavaLikeExtensions() Java-like extensions}) differ subtly * from ones obtained from pre-compiled binary (".class") files in class names are * usually left unresolved in the former. For example, the normal resolved form * of the type "String" embeds the class's package name ("Ljava.lang.String;" * or "Ljava/lang/String;"), whereas the unresolved form contains only what is * written "QString;". *

*

* Generic types introduce to the Java language in J2SE 1.5 add three new * facets to signatures: type variables, parameterized types with type arguments, * and formal type parameters. Rich signatures containing these facets * only occur when dealing with code that makes overt use of the new language * features. All other code, and certainly all Java code written or compiled * with J2SE 1.4 or earlier, involved only simple signatures. *

*

* Note that the "Q" and "!" formats are specific to Eclipse; the remainder * are specified in the JVM spec. *

*

* The syntax for a type signature is: *

 * TypeSignature ::=
 *     "B"  // byte
 *   | "C"  // char
 *   | "D"  // double
 *   | "F"  // float
 *   | "I"  // int
 *   | "J"  // long
 *   | "S"  // short
 *   | "V"  // void
 *   | "Z"  // boolean
 *   | "T" + Identifier + ";" // type variable
 *   | "[" + TypeSignature  // array X[]
 *   | "!" + TypeSignature  // capture-of ?
 *   | ResolvedClassTypeSignature
 *   | UnresolvedClassTypeSignature
 *
 * ResolvedClassTypeSignature ::= // resolved named type (in compiled code)
 *     "L" + Identifier + OptionalTypeArguments
 *           ( ( "." | "/" ) + Identifier + OptionalTypeArguments )* + ";"
 *     | OptionalTypeParameters + "L" + Identifier +
 *           ( ( "." | "/" ) + Identifier )* + ";"
 *
 * UnresolvedClassTypeSignature ::= // unresolved named type (in source code)
 *     "Q" + Identifier + OptionalTypeArguments
 *           ( ( "." | "/" ) + Identifier + OptionalTypeArguments )* + ";"
 *     | OptionalTypeParameters "Q" + Identifier +
 *           ( ( "." | "/" ) + Identifier )* + ";"
 *
 * OptionalTypeArguments ::=
 *     "<" + TypeArgument+ + ">"
 *   |
 *
 * TypeArgument ::=
 *   | TypeSignature
 *   | "*" // wildcard ?
 *   | "+" TypeSignature // wildcard ? extends X
 *   | "-" TypeSignature // wildcard ? super X
 *
 * OptionalTypeParameters ::=
 *     "<" + FormalTypeParameterSignature+ + ">"
 *   |
 * 
*

*

* Examples: *

*

*

* The syntax for a method signature is: *

 * MethodSignature ::= OptionalTypeParameters + "(" + ParamTypeSignature* + ")" + ReturnTypeSignature
 * ParamTypeSignature ::= TypeSignature
 * ReturnTypeSignature ::= TypeSignature
 * 
*

* Examples: *

*

*

* The syntax for a formal type parameter signature is: *

 * FormalTypeParameterSignature ::=
 *     TypeVariableName + OptionalClassBound + InterfaceBound*
 * TypeVariableName ::= Identifier
 * OptionalClassBound ::=
 *     ":"
 *   | ":" + TypeSignature
 * InterfaceBound ::=
 *     ":" + TypeSignature
 * 
*

* Examples: *

*

*

* This class provides static methods and constants only. *

* @noinstantiate This class is not intended to be instantiated by clients. */ public final class Signature { /* * Character constant indicating the primitive type boolean in a signature. * Value is 'Z'. */ public static final char C_BOOLEAN = 'Z'; /* * Character constant indicating the primitive type byte in a signature. * Value is 'B'. */ public static final char C_BYTE = 'B'; /* * Character constant indicating the primitive type char in a signature. * Value is 'C'. */ public static final char C_CHAR = 'C'; /* * Character constant indicating the primitive type double in a signature. * Value is 'D'. */ public static final char C_DOUBLE = 'D'; /* * Character constant indicating the primitive type float in a signature. * Value is 'F'. */ public static final char C_FLOAT = 'F'; /* * Character constant indicating the primitive type int in a signature. * Value is 'I'. */ public static final char C_INT = 'I'; /* * Character constant indicating the semicolon in a signature. * Value is ';'. */ public static final char C_SEMICOLON = ';'; /* * Character constant indicating the colon in a signature. * Value is ':'. * @since 3.0 */ public static final char C_COLON = ':'; /* * Character constant indicating the primitive type long in a signature. * Value is 'J'. */ public static final char C_LONG = 'J'; /* * Character constant indicating the primitive type short in a signature. * Value is 'S'. */ public static final char C_SHORT = 'S'; /* * Character constant indicating result type void in a signature. * Value is 'V'. */ public static final char C_VOID = 'V'; /* * Character constant indicating the start of a resolved type variable in a * signature. Value is 'T'. * @since 3.0 */ public static final char C_TYPE_VARIABLE = 'T'; /* * Character constant indicating an unbound wildcard type argument * in a signature. * Value is '*'. * @since 3.0 */ public static final char C_STAR = '*'; /* * Character constant indicating an exception in a signature. * Value is '^'. * @since 3.1 */ public static final char C_EXCEPTION_START = '^'; /* * Character constant indicating a bound wildcard type argument * in a signature with extends clause. * Value is '+'. * @since 3.1 */ public static final char C_EXTENDS = '+'; /* * Character constant indicating a bound wildcard type argument * in a signature with super clause. * Value is '-'. * @since 3.1 */ public static final char C_SUPER = '-'; /* * Character constant indicating the dot in a signature. * Value is '.'. */ public static final char C_DOT = '.'; /* * Character constant indicating the dollar in a signature. * Value is '$'. */ public static final char C_DOLLAR = '$'; /* * Character constant indicating an array type in a signature. * Value is '['. */ public static final char C_ARRAY = '['; /* * Character constant indicating the start of a resolved, named type in a * signature. Value is 'L'. */ public static final char C_RESOLVED = 'L'; /* * Character constant indicating the start of an unresolved, named type in a * signature. Value is 'Q'. */ public static final char C_UNRESOLVED = 'Q'; /* * Character constant indicating the end of a named type in a signature. * Value is ';'. */ public static final char C_NAME_END = ';'; /* * Character constant indicating the start of a parameter type list in a * signature. Value is '('. */ public static final char C_PARAM_START = '('; /* * Character constant indicating the end of a parameter type list in a * signature. Value is ')'. */ public static final char C_PARAM_END = ')'; /* * Character constant indicating the start of a formal type parameter * (or type argument) list in a signature. Value is '<'. * @since 3.0 */ public static final char C_GENERIC_START = '<'; /* * Character constant indicating the end of a generic type list in a * signature. Value is '>'. * @since 3.0 */ public static final char C_GENERIC_END = '>'; /* * Character constant indicating a capture of a wildcard type in a * signature. Value is '!'. * @since 3.1 */ public static final char C_CAPTURE = '!'; /* * String constant for the signature of the primitive type boolean. * Value is "Z". */ public static final String SIG_BOOLEAN = "Z"; //$NON-NLS-1$ /* * String constant for the signature of the primitive type byte. * Value is "B". */ public static final String SIG_BYTE = "B"; //$NON-NLS-1$ /* * String constant for the signature of the primitive type char. * Value is "C". */ public static final String SIG_CHAR = "C"; //$NON-NLS-1$ /* * String constant for the signature of the primitive type double. * Value is "D". */ public static final String SIG_DOUBLE = "D"; //$NON-NLS-1$ /* * String constant for the signature of the primitive type float. * Value is "F". */ public static final String SIG_FLOAT = "F"; //$NON-NLS-1$ /* * String constant for the signature of the primitive type int. * Value is "I". */ public static final String SIG_INT = "I"; //$NON-NLS-1$ /* * String constant for the signature of the primitive type long. * Value is "J". */ public static final String SIG_LONG = "J"; //$NON-NLS-1$ /* * String constant for the signature of the primitive type short. * Value is "S". */ public static final String SIG_SHORT = "S"; //$NON-NLS-1$ /* String constant for the signature of result type void. * Value is "V". */ public static final String SIG_VOID = "V"; //$NON-NLS-1$ /* * Kind constant for a class type signature. * @see #getTypeSignatureKind(String) * @since 3.0 */ public static final int CLASS_TYPE_SIGNATURE = 1; /* * Kind constant for a base (primitive or void) type signature. * @see #getTypeSignatureKind(String) * @since 3.0 */ public static final int BASE_TYPE_SIGNATURE = 2; /* * Kind constant for a type variable signature. * @see #getTypeSignatureKind(String) * @since 3.0 */ public static final int TYPE_VARIABLE_SIGNATURE = 3; /* * Kind constant for an array type signature. * @see #getTypeSignatureKind(String) * @since 3.0 */ public static final int ARRAY_TYPE_SIGNATURE = 4; /* * Kind constant for a wildcard type signature. * @see #getTypeSignatureKind(String) * @since 3.1 */ public static final int WILDCARD_TYPE_SIGNATURE = 5; /* * Kind constant for the capture of a wildcard type signature. * @see #getTypeSignatureKind(String) * @since 3.1 */ public static final int CAPTURE_TYPE_SIGNATURE = 6; private static final char[] BOOLEAN = "boolean".toCharArray(); //$NON-NLS-1$ private static final char[] BYTE = "byte".toCharArray(); //$NON-NLS-1$ private static final char[] CHAR = "char".toCharArray(); //$NON-NLS-1$ private static final char[] DOUBLE = "double".toCharArray(); //$NON-NLS-1$ private static final char[] FLOAT = "float".toCharArray(); //$NON-NLS-1$ private static final char[] INT = "int".toCharArray(); //$NON-NLS-1$ private static final char[] LONG = "long".toCharArray(); //$NON-NLS-1$ private static final char[] SHORT = "short".toCharArray(); //$NON-NLS-1$ private static final char[] VOID = "void".toCharArray(); //$NON-NLS-1$ // private static final char[] EXTENDS = "extends".toCharArray(); //$NON-NLS-1$ // private static final char[] SUPER = "super".toCharArray(); //$NON-NLS-1$ private static final char[] CAPTURE = "capture-of".toCharArray(); //$NON-NLS-1$ /* * Returns the number of parameter types in the given method signature. * * @param methodSignature the method signature * @return the number of parameters * @exception IllegalArgumentException if the signature is not syntactically * correct * @since 2.0 */ public static int getParameterCount(char[] methodSignature) throws IllegalArgumentException { try { int count = 0; int i = CharOperation.indexOf(C_PARAM_START, methodSignature); if (i < 0) { throw new IllegalArgumentException(); } i++; for (;;) { if (methodSignature[i] == C_PARAM_END) { return count; } int e = Utility.scanTypeSignature(methodSignature, i); if (e < 0) { throw new IllegalArgumentException(); } i = e + 1; count++; } } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException(); } } /* * Extracts the parameter type signatures from the given method signature. * The method signature is expected to be dot-based. * * @param methodSignature the method signature * @return the list of parameter type signatures * @exception IllegalArgumentException if the signature is syntactically * incorrect * * @since 2.0 */ public static char[][] getParameterTypes(char[] methodSignature) throws IllegalArgumentException { try { int count = getParameterCount(methodSignature); char[][] result = new char[count][]; if (count == 0) { return result; } int i = CharOperation.indexOf(C_PARAM_START, methodSignature); if (i < 0) { throw new IllegalArgumentException(); } i++; int t = 0; for (;;) { if (methodSignature[i] == C_PARAM_END) { return result; } int e = Utility.scanTypeSignature(methodSignature, i); if (e < 0) { throw new IllegalArgumentException(); } result[t] = CharOperation.subarray(methodSignature, i, e + 1); t++; i = e + 1; } } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException(); } } /* * Extracts the return type from the given method signature. The method signature is * expected to be dot-based. * * @param methodSignature the method signature * @return the type signature of the return type * @exception IllegalArgumentException if the signature is syntactically * incorrect * * @since 2.0 */ public static char[] getReturnType(char[] methodSignature) throws IllegalArgumentException { // skip type parameters int paren = CharOperation.lastIndexOf(C_PARAM_END, methodSignature); if (paren == -1) { throw new IllegalArgumentException(); } // there could be thrown exceptions behind, thus scan one type exactly int last = Utility.scanTypeSignature(methodSignature, paren + 1); return CharOperation.subarray(methodSignature, paren + 1, last + 1); } /* * Extracts the class and interface bounds from the given formal type * parameter signature. The class bound, if present, is listed before * the interface bounds. The signature is expected to be dot-based. * * @param formalTypeParameterSignature the formal type parameter signature * @return the (possibly empty) list of type signatures for the bounds * @exception IllegalArgumentException if the signature is syntactically * incorrect * @since 3.0 */ public static char[][] getTypeParameterBounds(char[] formalTypeParameterSignature) throws IllegalArgumentException { int p1 = CharOperation.indexOf(C_COLON, formalTypeParameterSignature); if (p1 < 0) { // no ":" means can't be a formal type parameter signature throw new IllegalArgumentException(); } if (p1 == formalTypeParameterSignature.length - 1) { // no class or interface bounds return CharOperation.NO_CHAR_CHAR; } int p2 = CharOperation.indexOf(C_COLON, formalTypeParameterSignature, p1 + 1); char[] classBound; if (p2 < 0) { // no interface bounds classBound = CharOperation.subarray(formalTypeParameterSignature, p1 + 1, formalTypeParameterSignature.length); return new char[][] {classBound}; } if (p2 == p1 + 1) { // no class bound, but 1 or more interface bounds classBound = null; } else { classBound = CharOperation.subarray(formalTypeParameterSignature, p1 + 1, p2); } char[][] interfaceBounds = CharOperation.splitOn(C_COLON, formalTypeParameterSignature, p2 + 1, formalTypeParameterSignature.length); if (classBound == null) { return interfaceBounds; } int resultLength = interfaceBounds.length + 1; char[][] result = new char[resultLength][]; result[0] = classBound; System.arraycopy(interfaceBounds, 0, result, 1, interfaceBounds.length); return result; } /* * Extracts the type parameter signatures from the given method or type signature. * The method or type signature is expected to be dot-based. * * @param methodOrTypeSignature the method or type signature * @return the list of type parameter signatures * @exception IllegalArgumentException if the signature is syntactically * incorrect * * @since 3.1 */ public static char[][] getTypeParameters(char[] methodOrTypeSignature) throws IllegalArgumentException { try { int length = methodOrTypeSignature.length; if (length == 0) return CharOperation.NO_CHAR_CHAR; if (methodOrTypeSignature[0] != C_GENERIC_START) return CharOperation.NO_CHAR_CHAR; ArrayList paramList = new ArrayList<>(1); int paramStart = 1, i = 1; // start after leading '<' while (i < length) { if (methodOrTypeSignature[i] == C_GENERIC_END) { int size = paramList.size(); if (size == 0) throw new IllegalArgumentException(); char[][] result; paramList.toArray(result = new char[size][]); return result; } i = CharOperation.indexOf(C_COLON, methodOrTypeSignature, i); if (i < 0 || i >= length) throw new IllegalArgumentException(); // iterate over bounds while (methodOrTypeSignature[i] == ':') { i++; // skip colon switch (methodOrTypeSignature[i]) { case ':' : // no class bound break; case C_GENERIC_END : break; case C_RESOLVED : try { i = Utility.scanClassTypeSignature(methodOrTypeSignature, i); i++; // position at start of next param if any } catch (IllegalArgumentException e) { // not a class type signature -> it is a new type parameter } break; case C_ARRAY : try { i = Utility.scanArrayTypeSignature(methodOrTypeSignature, i); i++; // position at start of next param if any } catch (IllegalArgumentException e) { // not an array type signature -> it is a new type parameter } break; case C_TYPE_VARIABLE : try { i = Utility.scanTypeVariableSignature(methodOrTypeSignature, i); i++; // position at start of next param if any } catch (IllegalArgumentException e) { // not a type variable signature -> it is a new type parameter } break; // default: another type parameter is starting } } paramList.add(CharOperation.subarray(methodOrTypeSignature, paramStart, i)); paramStart = i; // next param start from here } } catch (ArrayIndexOutOfBoundsException e) { // invalid signature, fall through } throw new IllegalArgumentException(); } /* * Converts the given type signature to a readable string. The signature is expected to * be dot-based. * *

* For example: *

	 * 
	 * toString({'[', 'L', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', ';'}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', '[', ']'}
	 * toString({'I'}) -> {'i', 'n', 't'}
	 * toString({'+', 'L', 'O', 'b', 'j', 'e', 'c', 't', ';'}) -> {'?', ' ', 'e', 'x', 't', 'e', 'n', 'd', 's', ' ', 'O', 'b', 'j', 'e', 'c', 't'}
	 * 
	 * 
*

*

* Note: This method assumes that a type signature containing a '$' * is an inner type signature. While this is correct in most cases, someone could * define a non-inner type name containing a '$'. Handling this * correctly in all cases would have required resolving the signature, which * generally not feasible. *

* * @param signature the type signature * @return the string representation of the type * @exception IllegalArgumentException if the signature is not syntactically * correct * * @since 2.0 */ public static char[] toCharArray(char[] signature) throws IllegalArgumentException { int sigLength = signature.length; if (sigLength == 0 || signature[0] == C_PARAM_START || signature[0] == C_GENERIC_START) { return toCharArray(signature, CharOperation.NO_CHAR, null, true, true); } StringBuffer buffer = new StringBuffer(signature.length + 10); appendTypeSignature(signature, 0, true, buffer); char[] result = new char[buffer.length()]; buffer.getChars(0, buffer.length(), result, 0); return result; } /* * Converts the given method signature to a readable form. The method signature is expected to * be dot-based. *

* For example: *

	 * 
	 * toString("([Ljava.lang.String;)V", "main", new String[] {"args"}, false, true) -> "void main(String[] args)"
	 * 
	 * 
*

* * @param methodSignature the method signature to convert * @param methodName the name of the method to insert in the result, or * null if no method name is to be included * @param parameterNames the parameter names to insert in the result, or * null if no parameter names are to be included; if supplied, * the number of parameter names must match that of the method signature * @param fullyQualifyTypeNames true if type names should be fully * qualified, and false to use only simple names * @param includeReturnType true if the return type is to be * included * @param isVargArgs true if the last argument should be displayed as a * variable argument, false otherwise. * @return the char array representation of the method signature * * @since 3.1 */ public static char[] toCharArray(char[] methodSignature, char[] methodName, char[][] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType, boolean isVargArgs) { int firstParen = CharOperation.indexOf(C_PARAM_START, methodSignature); if (firstParen == -1) { throw new IllegalArgumentException(); } StringBuffer buffer = new StringBuffer(methodSignature.length + 10); // return type if (includeReturnType) { char[] rts = getReturnType(methodSignature); appendTypeSignature(rts, 0, fullyQualifyTypeNames, buffer); buffer.append(' '); } // selector if (methodName != null) { buffer.append(methodName); } // parameters buffer.append('('); char[][] pts = getParameterTypes(methodSignature); // search for the last array in the signature int max = pts.length; int index = max - 1; loop: for (int i = index; i >= 0; i--) { if (pts[i][0] == Signature.C_ARRAY) { break loop; } index--; } for (int i = 0; i < max; i++) { if (i == index) { appendTypeSignature(pts[i], 0, fullyQualifyTypeNames, buffer, isVargArgs); } else { appendTypeSignature(pts[i], 0, fullyQualifyTypeNames, buffer); } if (parameterNames != null) { buffer.append(' '); buffer.append(parameterNames[i]); } if (i != pts.length - 1) { buffer.append(','); buffer.append(' '); } } buffer.append(')'); char[] result = new char[buffer.length()]; buffer.getChars(0, buffer.length(), result, 0); return result; } /* * Scans the given string for a type signature starting at the given * index and appends it to the given buffer, and returns the index of the last * character. * * @param string the signature string * @param start the 0-based character index of the first character * @param fullyQualifyTypeNames true if type names should be fully * qualified, and false to use only simple names * @param buffer the string buffer to append to * @return the 0-based character index of the last character * @exception IllegalArgumentException if this is not a type signature * @see Utility#scanTypeSignature(char[], int) */ private static int appendTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { return appendTypeSignature(string, start, fullyQualifyTypeNames, buffer, false); } /* * Scans the given string for a type signature starting at the given * index and appends it to the given buffer, and returns the index of the last * character. * * @param string the signature string * @param start the 0-based character index of the first character * @param fullyQualifyTypeNames true if type names should be fully * qualified, and false to use only simple names * @param buffer the string buffer to append to * @param isVarArgs true if the type must be displayed as a * variable argument, false otherwise. In this case, the type must be an array type * @return the 0-based character index of the last character * @exception IllegalArgumentException if this is not a type signature, or if isVarArgs is true, * and the type is not an array type signature. * @see Utility#scanTypeSignature(char[], int) */ private static int appendTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer, boolean isVarArgs) { // need a minimum 1 char if (start >= string.length) { throw new IllegalArgumentException(); } char c = string[start]; if (isVarArgs) { switch (c) { case C_ARRAY : return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer, true); case C_RESOLVED : case C_UNRESOLVED : case C_TYPE_VARIABLE : case C_BOOLEAN : case C_BYTE : case C_CHAR : case C_DOUBLE : case C_FLOAT : case C_INT : case C_LONG : case C_SHORT : case C_VOID : case C_STAR : case C_EXTENDS : case C_SUPER : case C_CAPTURE : default : throw new IllegalArgumentException(); // a var args is an array type } } switch (c) { case C_ARRAY : return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer); case C_RESOLVED : case C_UNRESOLVED : return appendClassTypeSignature(string, start, fullyQualifyTypeNames, buffer); case C_TYPE_VARIABLE : int e = Utility.scanTypeVariableSignature(string, start); buffer.append(string, start + 1, e - start - 1); return e; case C_BOOLEAN : buffer.append(BOOLEAN); return start; case C_BYTE : buffer.append(BYTE); return start; case C_CHAR : buffer.append(CHAR); return start; case C_DOUBLE : buffer.append(DOUBLE); return start; case C_FLOAT : buffer.append(FLOAT); return start; case C_INT : buffer.append(INT); return start; case C_LONG : buffer.append(LONG); return start; case C_SHORT : buffer.append(SHORT); return start; case C_VOID : buffer.append(VOID); return start; case C_CAPTURE : return appendCaptureTypeSignature(string, start, fullyQualifyTypeNames, buffer); case C_STAR : case C_EXTENDS : case C_SUPER : return appendTypeArgumentSignature(string, start, fullyQualifyTypeNames, buffer); default : throw new IllegalArgumentException(); } } /* * Scans the given string for an array type signature starting at the given * index and appends it to the given buffer, and returns the index of the last * character. * * @param string the signature string * @param start the 0-based character index of the first character * @param fullyQualifyTypeNames true if type names should be fully * qualified, and false to use only simple names * @return the 0-based character index of the last character * @exception IllegalArgumentException if this is not an array type signature * @see Utility#scanArrayTypeSignature(char[], int) */ private static int appendArrayTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer, false); } /* * Scans the given string for an array type signature starting at the given * index and appends it to the given buffer, and returns the index of the last * character. * * @param string the signature string * @param start the 0-based character index of the first character * @param fullyQualifyTypeNames true if type names should be fully * qualified, and false to use only simple names * @return the 0-based character index of the last character * @exception IllegalArgumentException if this is not an array type signature * @see Utility#scanArrayTypeSignature(char[], int) */ private static int appendCaptureTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { // need a minimum 2 char if (start >= string.length - 1) { throw new IllegalArgumentException(); } char c = string[start]; if (c != C_CAPTURE) { throw new IllegalArgumentException(); } buffer.append(CAPTURE).append(' '); return appendTypeArgumentSignature(string, start + 1, fullyQualifyTypeNames, buffer); } /* * Scans the given string for an array type signature starting at the given * index and appends it to the given buffer, and returns the index of the last * character. * * @param string the signature string * @param start the 0-based character index of the first character * @param fullyQualifyTypeNames true if type names should be fully * qualified, and false to use only simple names * @param isVarArgs true if the array type must be displayed as a * variable argument, false otherwise * @return the 0-based character index of the last character * @exception IllegalArgumentException if this is not an array type signature * @see Utility#scanArrayTypeSignature(char[], int) */ private static int appendArrayTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer, boolean isVarArgs) { int length = string.length; // need a minimum 2 char if (start >= length - 1) { throw new IllegalArgumentException(); } char c = string[start]; if (c != C_ARRAY) { throw new IllegalArgumentException(); } int index = start; c = string[++index]; while (c == C_ARRAY) { // need a minimum 2 char if (index >= length - 1) { throw new IllegalArgumentException(); } c = string[++index]; } int e = appendTypeSignature(string, index, fullyQualifyTypeNames, buffer); for (int i = 1, dims = index - start; i < dims; i++) { buffer.append('[').append(']'); } if (isVarArgs) { buffer.append('.').append('.').append('.'); } else { buffer.append('[').append(']'); } return e; } /* * Scans the given string for a class type signature starting at the given * index and appends it to the given buffer, and returns the index of the last * character. * * @param string the signature string * @param start the 0-based character index of the first character * @param fullyQualifyTypeNames true if type names should be fully * qualified, and false to use only simple names * @param buffer the string buffer to append to * @return the 0-based character index of the last character * @exception IllegalArgumentException if this is not a class type signature * @see Utility#scanClassTypeSignature(char[], int) */ private static int appendClassTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { // need a minimum 3 chars "Lx;" if (start >= string.length - 2) { throw new IllegalArgumentException(); } // must start in "L" or "Q" char c = string[start]; if (c != C_RESOLVED && c != C_UNRESOLVED) { throw new IllegalArgumentException(); } boolean resolved = (c == C_RESOLVED); boolean removePackageQualifiers = !fullyQualifyTypeNames; if (!resolved) { // keep everything in an unresolved name removePackageQualifiers = false; } int p = start + 1; int checkpoint = buffer.length(); int innerTypeStart = -1; boolean inAnonymousType = false; while (true) { if (p >= string.length) { throw new IllegalArgumentException(); } c = string[p]; switch (c) { case C_SEMICOLON : // all done return p; case C_GENERIC_START : int e = appendTypeArgumentSignatures(string, p, fullyQualifyTypeNames, buffer); // once we hit type arguments there are no more package prefixes removePackageQualifiers = false; p = e; break; case C_DOT : if (removePackageQualifiers) { // erase package prefix buffer.setLength(checkpoint); } else { buffer.append('.'); } break; case '/' : if (removePackageQualifiers) { // erase package prefix buffer.setLength(checkpoint); } else { buffer.append('/'); } break; case C_DOLLAR : innerTypeStart = buffer.length(); inAnonymousType = false; if (resolved) { // once we hit "$" there are no more package prefixes removePackageQualifiers = false; /* * Convert '$' in resolved type signatures into '.'. * NOTE: This assumes that the type signature is an inner type * signature. This is true in most cases, but someone can define a * non-inner type name containing a '$'. */ buffer.append('.'); } break; default : if (innerTypeStart != -1 && !inAnonymousType && Character.isDigit(c)) { inAnonymousType = true; buffer.setLength(innerTypeStart); // remove '.' buffer.insert(checkpoint, "new "); //$NON-NLS-1$ buffer.append("(){}"); //$NON-NLS-1$ } if (!inAnonymousType) buffer.append(c); innerTypeStart = -1; } p++; } } /* * Scans the given string for a list of type arguments signature starting at the * given index and appends it to the given buffer, and returns the index of the * last character. * * @param string the signature string * @param start the 0-based character index of the first character * @param fullyQualifyTypeNames true if type names should be fully * qualified, and false to use only simple names * @param buffer the string buffer to append to * @return the 0-based character index of the last character * @exception IllegalArgumentException if this is not a list of type argument * signatures * @see Utility#scanTypeArgumentSignatures(char[], int) */ private static int appendTypeArgumentSignatures(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { // need a minimum 2 char "<>" if (start >= string.length - 1) { throw new IllegalArgumentException(); } char c = string[start]; if (c != C_GENERIC_START) { throw new IllegalArgumentException(); } buffer.append('<'); int p = start + 1; int count = 0; while (true) { if (p >= string.length) { throw new IllegalArgumentException(); } c = string[p]; if (c == C_GENERIC_END) { buffer.append('>'); return p; } if (count != 0) { buffer.append(','); } int e = appendTypeArgumentSignature(string, p, fullyQualifyTypeNames, buffer); count++; p = e + 1; } } /* * Scans the given string for a type argument signature starting at the given * index and appends it to the given buffer, and returns the index of the last * character. * * @param string the signature string * @param start the 0-based character index of the first character * @param fullyQualifyTypeNames true if type names should be fully * qualified, and false to use only simple names * @param buffer the string buffer to append to * @return the 0-based character index of the last character * @exception IllegalArgumentException if this is not a type argument signature * @see Utility#scanTypeArgumentSignature(char[], int) */ private static int appendTypeArgumentSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) { // need a minimum 1 char if (start >= string.length) { throw new IllegalArgumentException(); } char c = string[start]; switch (c) { case C_STAR : buffer.append('?'); return start; case C_EXTENDS : buffer.append("? extends "); //$NON-NLS-1$ return appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer); case C_SUPER : buffer.append("? super "); //$NON-NLS-1$ return appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer); default : return appendTypeSignature(string, start, fullyQualifyTypeNames, buffer); } } /* * Converts the given method signature to a readable form. The method signature is expected to * be dot-based. *

* For example: *

	 * 
	 * toString("([Ljava.lang.String;)V", "main", new String[] {"args"}, false, true) -> "void main(String[] args)"
	 * 
	 * 
*

* * @param methodSignature the method signature to convert * @param methodName the name of the method to insert in the result, or * null if no method name is to be included * @param parameterNames the parameter names to insert in the result, or * null if no parameter names are to be included; if supplied, * the number of parameter names must match that of the method signature * @param fullyQualifyTypeNames true if type names should be fully * qualified, and false to use only simple names * @param includeReturnType true if the return type is to be * included * @return the char array representation of the method signature * * @since 2.0 */ public static char[] toCharArray(char[] methodSignature, char[] methodName, char[][] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType) { return toCharArray(methodSignature, methodName, parameterNames, fullyQualifyTypeNames, includeReturnType, false); } }