/******************************************************************************* * 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: *
"[[I"
denotes int[][]
"Ljava.lang.String;"
denotes java.lang.String
in compiled code"QString;"
denotes String
in source code"Qjava.lang.String;"
denotes java.lang.String
in source code"[QString;"
denotes String[]
in source code"QMap<QString;*>;"
denotes Map<String,?>
in source code"Qjava.util.List<TV;>;"
denotes java.util.List<V>
in source code"<E;>Ljava.util.List;"
denotes <E>java.util.List
in source code* The syntax for a method signature is: *
* MethodSignature ::= OptionalTypeParameters + "(" + ParamTypeSignature* + ")" + ReturnTypeSignature * ParamTypeSignature ::= TypeSignature * ReturnTypeSignature ::= TypeSignature **
* Examples: *
"()I"
denotes int foo()
"([Ljava.lang.String;)V"
denotes void foo(java.lang.String[])
in compiled code"(QString;)QObject;"
denotes Object foo(String)
in source code* The syntax for a formal type parameter signature is: *
* FormalTypeParameterSignature ::= * TypeVariableName + OptionalClassBound + InterfaceBound* * TypeVariableName ::= Identifier * OptionalClassBound ::= * ":" * | ":" + TypeSignature * InterfaceBound ::= * ":" + TypeSignature **
* Examples: *
"X:"
denotes X
"X:QReader;"
denotes X extends Reader
in source code"X:QReader;:QSerializable;"
denotes X extends Reader & Serializable
in source code* 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* 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.
*
* 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);
}
}