import eclipse 3.1 M4 compiler
[org.ibex.tool.git] / src / org / eclipse / jdt / core / Signature.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *     IBM Corporation - added J2SE 1.5 support
11  *******************************************************************************/
12 package org.eclipse.jdt.core;
13
14 import java.util.ArrayList;
15
16 import org.eclipse.jdt.core.compiler.CharOperation;
17
18
19 /**
20  * Provides methods for encoding and decoding type and method signature strings.
21  * <p>
22  * Signatures obtained from parsing source (".java") files differ subtly from
23  * ones obtained from pre-compiled binary (".class") files in class names are
24  * usually left unresolved in the former. For example, the normal resolved form
25  * of the type "String" embeds the class's package name ("Ljava.lang.String;"
26  * or "Ljava/lang/String;"), whereas the unresolved form contains only what is
27  * written "QString;".
28  * </p>
29  * <p>
30  * Generic types introduce to the Java language in J2SE 1.5 add three new
31  * facets to signatures: type variables, parameterized types with type arguments,
32  * and formal type parameters. <it>Rich</it> signatures containing these facets
33  * only occur when dealing with code that makes overt use of the new language
34  * features. All other code, and certainly all Java code written or compiled
35  * with J2SE 1.4 or earlier, involved only <it>simple</it> signatures.
36  * </p>
37  * <p>
38  * The syntax for a type signature is:
39  * <pre>
40  * TypeSignature ::=
41  *     "B"  // byte
42  *   | "C"  // char
43  *   | "D"  // double
44  *   | "F"  // float
45  *   | "I"  // int
46  *   | "J"  // long
47  *   | "S"  // short
48  *   | "V"  // void
49  *   | "Z"  // boolean
50  *   | "T" + Identifier + ";" // type variable
51  *   | "[" + TypeSignature  // array X[]
52  *   | ResolvedClassTypeSignature
53  *   | UnresolvedClassTypeSignature
54  * 
55  * ResolvedClassTypeSignature ::= // resolved named type (in compiled code)
56  *     "L" + Identifier + OptionalTypeArguments
57  *           ( ( "." | "/" ) + Identifier + OptionalTypeArguments )* + ";"
58  * 
59  * UnresolvedClassTypeSignature ::= // unresolved named type (in source code)
60  *     "Q" + Identifier + OptionalTypeArguments
61  *           ( ( "." | "/" ) + Identifier + OptionalTypeArguments )* + ";"
62  * 
63  * OptionalTypeArguments ::=
64  *     "&lt;" + TypeArgument+ + "&gt;" 
65  *   |
66  * 
67  * TypeArgument ::=
68  *   | TypeSignature
69  *   | "*" // wildcard ?
70  *   | "+" TypeSignature // wildcard ? extends X
71  *   | "-" TypeSignature // wildcard ? super X
72  * </pre>
73  * </p>
74  * <p>
75  * Examples:
76  * <ul>
77  *   <li><code>"[[I"</code> denotes <code>int[][]</code></li>
78  *   <li><code>"Ljava.lang.String;"</code> denotes <code>java.lang.String</code> in compiled code</li>
79  *   <li><code>"QString;"</code> denotes <code>String</code> in source code</li>
80  *   <li><code>"Qjava.lang.String;"</code> denotes <code>java.lang.String</code> in source code</li>
81  *   <li><code>"[QString;"</code> denotes <code>String[]</code> in source code</li>
82  *   <li><code>"QMap&lt;QString;&ast;&gt;;"</code> denotes <code>Map&lt;String,?&gt;</code> in source code</li>
83  *   <li><code>"Qjava.util.List&ltTV;&gt;;"</code> denotes <code>java.util.List&lt;V&gt;</code> in source code</li>
84  * </ul>
85  * </p>
86  * <p>
87  * The syntax for a method signature is: 
88  * <pre>
89  * MethodSignature ::= "(" + ParamTypeSignature* + ")" + ReturnTypeSignature
90  * ParamTypeSignature ::= TypeSignature
91  * ReturnTypeSignature ::= TypeSignature
92  * </pre>
93  * <p>
94  * Examples:
95  * <ul>
96  *   <li><code>"()I"</code> denotes <code>int foo()</code></li>
97  *   <li><code>"([Ljava.lang.String;)V"</code> denotes <code>void foo(java.lang.String[])</code> in compiled code</li>
98  *   <li><code>"(QString;)QObject;"</code> denotes <code>Object foo(String)</code> in source code</li>
99  * </ul>
100  * </p>
101  * <p>
102  * The syntax for a formal type parameter signature is:
103  * <pre>
104  * FormalTypeParameterSignature ::=
105  *     TypeVariableName + OptionalClassBound + InterfaceBound*
106  * TypeVariableName ::= Identifier
107  * OptionalClassBound ::=
108  *     ":"
109  *   | ":" + TypeSignature
110  * InterfaceBound ::= 
111  *     ":" + TypeSignature
112  * </pre>
113  * <p>
114  * Examples:
115  * <ul>
116  *   <li><code>"X:"</code> denotes <code>X</code></li>
117  *   <li><code>"X:QReader;"</code> denotes <code>X extends Reader</code> in source code</li>
118  *   <li><code>"X:QReader;:QSerializable;"</code> denotes <code>X extends Reader & Serializable</code> in source code</li>
119  * </ul>
120  * </p>
121  * <p>
122  * This class provides static methods and constants only; it is not intended to be
123  * instantiated or subclassed by clients.
124  * </p>
125  */
126 public final class Signature {
127
128         /**
129          * Character constant indicating the primitive type boolean in a signature.
130          * Value is <code>'Z'</code>.
131          */
132         public static final char C_BOOLEAN              = 'Z';
133
134         /**
135          * Character constant indicating the primitive type byte in a signature.
136          * Value is <code>'B'</code>.
137          */
138         public static final char C_BYTE                 = 'B';
139
140         /**
141          * Character constant indicating the primitive type char in a signature.
142          * Value is <code>'C'</code>.
143          */
144         public static final char C_CHAR                 = 'C';
145
146         /**
147          * Character constant indicating the primitive type double in a signature.
148          * Value is <code>'D'</code>.
149          */
150         public static final char C_DOUBLE               = 'D';
151
152         /**
153          * Character constant indicating the primitive type float in a signature.
154          * Value is <code>'F'</code>.
155          */
156         public static final char C_FLOAT                = 'F';
157
158         /**
159          * Character constant indicating the primitive type int in a signature.
160          * Value is <code>'I'</code>.
161          */
162         public static final char C_INT                  = 'I';
163         
164         /**
165          * Character constant indicating the semicolon in a signature.
166          * Value is <code>';'</code>.
167          */
168         public static final char C_SEMICOLON                    = ';';
169
170         /**
171          * Character constant indicating the colon in a signature.
172          * Value is <code>':'</code>.
173          * @since 3.0
174          */
175         public static final char C_COLON                        = ':';
176
177         /**
178          * Character constant indicating the primitive type long in a signature.
179          * Value is <code>'J'</code>.
180          */
181         public static final char C_LONG                 = 'J';
182         
183         /**
184          * Character constant indicating the primitive type short in a signature.
185          * Value is <code>'S'</code>.
186          */
187         public static final char C_SHORT                = 'S';
188         
189         /**
190          * Character constant indicating result type void in a signature.
191          * Value is <code>'V'</code>.
192          */
193         public static final char C_VOID                 = 'V';
194         
195         /**
196          * Character constant indicating the start of a resolved type variable in a 
197          * signature. Value is <code>'T'</code>.
198          * @since 3.0
199          */
200         public static final char C_TYPE_VARIABLE        = 'T';
201         
202         /**
203          * Character constant indicating an unbound wildcard type argument 
204          * in a signature.
205          * Value is <code>'&ast;'</code>.
206          * @since 3.0
207          */
208         public static final char C_STAR = '*';
209         
210         /**
211          * Character constant indicating a bound wildcard type argument 
212          * in a signature with extends clause.
213          * Value is <code>'+'</code>.
214          * @since 3.1
215          */
216         public static final char C_EXTENDS      = '+';
217         
218         /**
219          * Character constant indicating a bound wildcard type argument 
220          * in a signature with super clause.
221          * Value is <code>'-'</code>.
222          * @since 3.1
223          */
224         public static final char C_SUPER        = '-';
225         
226         /** 
227          * Character constant indicating the dot in a signature. 
228          * Value is <code>'.'</code>.
229          */
230         public static final char C_DOT                  = '.';
231         
232         /** 
233          * Character constant indicating the dollar in a signature.
234          * Value is <code>'$'</code>.
235          */
236         public static final char C_DOLLAR                       = '$';
237
238         /** 
239          * Character constant indicating an array type in a signature.
240          * Value is <code>'['</code>.
241          */
242         public static final char C_ARRAY                = '[';
243
244         /** 
245          * Character constant indicating the start of a resolved, named type in a 
246          * signature. Value is <code>'L'</code>.
247          */
248         public static final char C_RESOLVED             = 'L';
249
250         /** 
251          * Character constant indicating the start of an unresolved, named type in a
252          * signature. Value is <code>'Q'</code>.
253          */
254         public static final char C_UNRESOLVED   = 'Q';
255
256         /**
257          * Character constant indicating the end of a named type in a signature. 
258          * Value is <code>';'</code>.
259          */
260         public static final char C_NAME_END             = ';';
261
262         /**
263          * Character constant indicating the start of a parameter type list in a
264          * signature. Value is <code>'('</code>.
265          */
266         public static final char C_PARAM_START  = '(';
267
268         /**
269          * Character constant indicating the end of a parameter type list in a 
270          * signature. Value is <code>')'</code>.
271          */
272         public static final char C_PARAM_END    = ')';
273
274         /**
275          * Character constant indicating the start of a formal type parameter
276          * (or type argument) list in a signature. Value is <code>'&lt;'</code>.
277          * @since 3.0
278          */
279         public static final char C_GENERIC_START        = '<';
280
281         /**
282          * Character constant indicating the end of a generic type list in a 
283          * signature. Value is <code>'%gt;'</code>.
284          * @since 3.0
285          */
286         public static final char C_GENERIC_END  = '>';
287
288         /**
289          * String constant for the signature of the primitive type boolean.
290          * Value is <code>"Z"</code>.
291          */
292         public static final String SIG_BOOLEAN          = "Z"; //$NON-NLS-1$
293
294         /**
295          * String constant for the signature of the primitive type byte. 
296          * Value is <code>"B"</code>.
297          */
298         public static final String SIG_BYTE             = "B"; //$NON-NLS-1$
299
300         /**
301          * String constant for the signature of the primitive type char.
302          * Value is <code>"C"</code>.
303          */
304         public static final String SIG_CHAR             = "C"; //$NON-NLS-1$
305
306         /**
307          * String constant for the signature of the primitive type double.
308          * Value is <code>"D"</code>.
309          */
310         public static final String SIG_DOUBLE           = "D"; //$NON-NLS-1$
311
312         /**
313          * String constant for the signature of the primitive type float.
314          * Value is <code>"F"</code>.
315          */
316         public static final String SIG_FLOAT            = "F"; //$NON-NLS-1$
317
318         /**
319          * String constant for the signature of the primitive type int.
320          * Value is <code>"I"</code>.
321          */
322         public static final String SIG_INT                      = "I"; //$NON-NLS-1$
323
324         /**
325          * String constant for the signature of the primitive type long.
326          * Value is <code>"J"</code>.
327          */
328         public static final String SIG_LONG                     = "J"; //$NON-NLS-1$
329
330         /**
331          * String constant for the signature of the primitive type short.
332          * Value is <code>"S"</code>.
333          */
334         public static final String SIG_SHORT            = "S"; //$NON-NLS-1$
335
336         /** String constant for the signature of result type void.
337          * Value is <code>"V"</code>.
338          */
339         public static final String SIG_VOID                     = "V"; //$NON-NLS-1$
340         
341
342         /**
343          * Kind constant for a class type signature.
344          * @see #getTypeSignatureKind(String)
345          * @since 3.0
346          */
347         public static int CLASS_TYPE_SIGNATURE = 1;
348
349         /**
350          * Kind constant for a base (primitive or void) type signature.
351          * @see #getTypeSignatureKind(String)
352          * @since 3.0
353          */
354         public static int BASE_TYPE_SIGNATURE = 2;
355
356         /**
357          * Kind constant for a type variable signature.
358          * @see #getTypeSignatureKind(String)
359          * @since 3.0
360          */
361         public static int TYPE_VARIABLE_SIGNATURE = 3;
362
363         /**
364          * Kind constant for an array type signature.
365          * @see #getTypeSignatureKind(String)
366          * @since 3.0
367          */
368         public static int ARRAY_TYPE_SIGNATURE = 4;
369
370         private static final char[] BOOLEAN = "boolean".toCharArray(); //$NON-NLS-1$
371         private static final char[] BYTE = "byte".toCharArray(); //$NON-NLS-1$
372         private static final char[] CHAR = "char".toCharArray(); //$NON-NLS-1$
373         private static final char[] DOUBLE = "double".toCharArray(); //$NON-NLS-1$
374         private static final char[] FLOAT = "float".toCharArray(); //$NON-NLS-1$
375         private static final char[] INT = "int".toCharArray(); //$NON-NLS-1$
376         private static final char[] LONG = "long".toCharArray(); //$NON-NLS-1$
377         private static final char[] SHORT = "short".toCharArray(); //$NON-NLS-1$
378         private static final char[] VOID = "void".toCharArray(); //$NON-NLS-1$
379         private static final char[] EXTENDS = "extends".toCharArray(); //$NON-NLS-1$
380         private static final char[] SUPER = "super".toCharArray(); //$NON-NLS-1$
381         
382         private static final String EMPTY = new String(CharOperation.NO_CHAR);
383                 
384 private Signature() {
385         // Not instantiable
386 }
387
388 private static int checkName(char[] name, char[] typeName, int pos, int length) {
389     if (CharOperation.fragmentEquals(name, typeName, pos, true)) {
390         pos += name.length;
391         if (pos == length) return pos;
392         char currentChar = typeName[pos];
393         switch (currentChar) {
394             case ' ' :
395             case '.' :
396             case '<' :
397             case '>' :
398             case '[' :
399             case ',' :
400                 return pos;
401                         default:
402                             if (Character.isWhitespace(currentChar))
403                                 return pos;
404                             
405         }
406     }
407     return -1;
408 }
409
410 /**
411  * Creates a new type signature with the given amount of array nesting added 
412  * to the given type signature.
413  *
414  * @param typeSignature the type signature
415  * @param arrayCount the desired number of levels of array nesting
416  * @return the encoded array type signature
417  * 
418  * @since 2.0
419  */
420 public static char[] createArraySignature(char[] typeSignature, int arrayCount) {
421         if (arrayCount == 0) return typeSignature;
422         int sigLength = typeSignature.length;
423         char[] result = new char[arrayCount + sigLength];
424         for (int i = 0; i < arrayCount; i++) {
425                 result[i] = C_ARRAY;
426         }
427         System.arraycopy(typeSignature, 0, result, arrayCount, sigLength);
428         return result;
429 }
430 /**
431  * Creates a new type signature with the given amount of array nesting added 
432  * to the given type signature.
433  *
434  * @param typeSignature the type signature
435  * @param arrayCount the desired number of levels of array nesting
436  * @return the encoded array type signature
437  */
438 public static String createArraySignature(String typeSignature, int arrayCount) {
439         return new String(createArraySignature(typeSignature.toCharArray(), arrayCount));
440 }
441
442 /**
443  * Creates a method signature from the given parameter and return type 
444  * signatures. The encoded method signature is dot-based.
445  *
446  * @param parameterTypes the list of parameter type signatures
447  * @param returnType the return type signature
448  * @return the encoded method signature
449  * 
450  * @since 2.0
451  */
452 public static char[] createMethodSignature(char[][] parameterTypes, char[] returnType) {
453         int parameterTypesLength = parameterTypes.length;
454         int parameterLength = 0;
455         for (int i = 0; i < parameterTypesLength; i++) {
456                 parameterLength += parameterTypes[i].length;
457                 
458         }
459         int returnTypeLength = returnType.length;
460         char[] result = new char[1 + parameterLength + 1 + returnTypeLength];
461         result[0] = C_PARAM_START;
462         int index = 1;
463         for (int i = 0; i < parameterTypesLength; i++) {
464                 char[] parameterType = parameterTypes[i];
465                 int length = parameterType.length;
466                 System.arraycopy(parameterType, 0, result, index, length);
467                 index += length;
468         }
469         result[index] = C_PARAM_END;
470         System.arraycopy(returnType, 0, result, index+1, returnTypeLength);
471         return result;
472 }
473
474 /**
475  * Creates a method signature from the given parameter and return type 
476  * signatures. The encoded method signature is dot-based. This method
477  * is equivalent to
478  * <code>createMethodSignature(parameterTypes, returnType)</code>.
479  *
480  * @param parameterTypes the list of parameter type signatures
481  * @param returnType the return type signature
482  * @return the encoded method signature
483  * @see Signature#createMethodSignature(char[][], char[])
484  */
485 public static String createMethodSignature(String[] parameterTypes, String returnType) {
486         int parameterTypesLenth = parameterTypes.length;
487         char[][] parameters = new char[parameterTypesLenth][];
488         for (int i = 0; i < parameterTypesLenth; i++) {
489                 parameters[i] = parameterTypes[i].toCharArray();
490         }
491         return new String(createMethodSignature(parameters, returnType.toCharArray()));
492 }
493
494 /**
495  * Creates a new type parameter signature with the given name and bounds.
496  *
497  * @param typeParameterName the type parameter name
498  * @param boundSignatures the signatures of associated bounds or empty array if none
499  * @return the encoded type parameter signature
500  * 
501  * @since 3.1
502  */
503 public static char[] createTypeParameterSignature(char[] typeParameterName, char[][] boundSignatures) {
504         int length = boundSignatures.length;
505         if (length == 0) {
506                 return CharOperation.append(typeParameterName, C_COLON); // param signature with no bounds still gets trailing colon
507         }
508         int boundsSize = 0;
509         for (int i = 0; i < length; i++) {
510                 boundsSize += boundSignatures[i].length + 1;
511         }
512         int nameLength = typeParameterName.length;
513         char[] result = new char[nameLength + boundsSize];
514         System.arraycopy(typeParameterName, 0, result, 0, nameLength);
515         int index = nameLength;
516         for (int i = 0; i < length; i++) {
517                 result[index++] = C_COLON;
518                 int boundLength = boundSignatures[i].length;
519                 System.arraycopy(boundSignatures[i], 0, result, index, boundLength);
520                 index += boundLength;
521         }
522         return result;
523 }
524
525 /**
526  * Creates a new type parameter signature with the given name and bounds.
527  *
528  * @param typeParameterName the type parameter name
529  * @param boundSignatures the signatures of associated bounds or empty array if none
530  * @return the encoded type parameter signature
531  * 
532  * @since 3.1
533  */
534 public static String createTypeParameterSignature(String typeParameterName, String[] boundSignatures) {
535         int length = boundSignatures.length;
536         char[][] boundSignatureChars = new char[length][];
537         for (int i = 0; i < length; i++) {
538                 boundSignatureChars[i] = boundSignatures[i].toCharArray();
539         }
540         return new String(createTypeParameterSignature(typeParameterName.toCharArray(), boundSignatureChars));
541 }
542
543 /**
544  * Creates a new type signature from the given type name encoded as a character
545  * array. The type name may contain primitive types, array types or parameterized types.
546  * This method is equivalent to
547  * <code>createTypeSignature(new String(typeName),isResolved)</code>, although
548  * more efficient for callers with character arrays rather than strings. If the 
549  * type name is qualified, then it is expected to be dot-based.
550  *
551  * @param typeName the possibly qualified type name
552  * @param isResolved <code>true</code> if the type name is to be considered
553  *   resolved (for example, a type name from a binary class file), and 
554  *   <code>false</code> if the type name is to be considered unresolved
555  *   (for example, a type name found in source code)
556  * @return the encoded type signature
557  * @see #createTypeSignature(java.lang.String,boolean)
558  */
559 public static String createTypeSignature(char[] typeName, boolean isResolved) {
560         return new String(createCharArrayTypeSignature(typeName, isResolved));
561 }
562
563 /**
564  * Creates a new type signature from the given type name encoded as a character
565  * array. The type name may contain primitive types or array types or parameterized types.
566  * This method is equivalent to
567  * <code>createTypeSignature(new String(typeName),isResolved).toCharArray()</code>,
568  * although more efficient for callers with character arrays rather than strings.
569  * If the type name is qualified, then it is expected to be dot-based.
570  *
571  * @param typeName the possibly qualified type name
572  * @param isResolved <code>true</code> if the type name is to be considered
573  *   resolved (for example, a type name from a binary class file), and 
574  *   <code>false</code> if the type name is to be considered unresolved
575  *   (for example, a type name found in source code)
576  * @return the encoded type signature
577  * @see #createTypeSignature(java.lang.String,boolean)
578  * 
579  * @since 2.0
580  */
581 public static char[] createCharArrayTypeSignature(char[] typeName, boolean isResolved) {
582         if (typeName == null) throw new IllegalArgumentException("null"); //$NON-NLS-1$
583         int length = typeName.length;
584         if (length == 0) throw new IllegalArgumentException(new String(typeName));
585         StringBuffer buffer = new StringBuffer(5);
586         int pos = encodeTypeSignature(typeName, 0, isResolved, length, buffer);
587         pos = consumeWhitespace(typeName, pos, length);
588         if (pos < length) throw new IllegalArgumentException(new String(typeName));
589         char[] result = new char[length = buffer.length()];
590         buffer.getChars(0, length, result, 0);
591         return result;  
592 }
593 private static int consumeWhitespace(char[] typeName, int pos, int length) {
594     while (pos < length) {
595         char currentChar = typeName[pos];
596         if (currentChar != ' ' && !CharOperation.isWhitespace(currentChar)) {
597             break;
598         }
599         pos++;
600     }
601     return pos;
602 }
603 private static int encodeQualifiedName(char[] typeName, int pos, int length, StringBuffer buffer) {
604     int count = 0;
605     char lastAppendedChar = 0;
606     nameLoop: while (pos < length) {
607             char currentChar = typeName[pos];
608                 switch (currentChar) {
609                     case '<' :
610                     case '>' :
611                     case '[' :
612                     case ',' :
613                         break nameLoop;
614                         case '.' :
615                             buffer.append(C_DOT);
616                                 lastAppendedChar = C_DOT;
617                             count++;
618                             break;
619                         default:
620                             if (currentChar == ' ' || Character.isWhitespace(currentChar)) {
621                                 if (lastAppendedChar == C_DOT) { // allow spaces after a dot
622                                     pos = consumeWhitespace(typeName, pos, length) - 1; // will be incremented
623                                     break; 
624                                 }
625                                 // allow spaces before a dot
626                                     int checkPos = checkNextChar(typeName, '.', pos, length, true);
627                                     if (checkPos > 0) {
628                                         buffer.append(C_DOT);                   // process dot immediately to avoid one iteration
629                                         lastAppendedChar = C_DOT;
630                                         count++;
631                                         pos = checkPos;
632                                         break;
633                                     }
634                                     break nameLoop;
635                             }
636                             buffer.append(currentChar);
637                             lastAppendedChar = currentChar;
638                                 count++;
639                             break;
640                 }
641             pos++;
642     }
643     if (count == 0) throw new IllegalArgumentException(new String(typeName));
644         return pos;
645 }
646
647 private static int encodeArrayDimension(char[] typeName, int pos, int length, StringBuffer buffer) {
648     int checkPos;
649     while (pos < length && (checkPos = checkNextChar(typeName, '[', pos, length, true)) > 0) {
650         pos = checkNextChar(typeName, ']', checkPos, length, false);
651         buffer.append(C_ARRAY);
652     }
653     return pos;
654 }
655 private static int checkArrayDimension(char[] typeName, int pos, int length) {
656     int genericBalance = 0;
657     while (pos < length) {
658                 switch(typeName[pos]) {
659                     case '<' :
660                         genericBalance++;
661                         break;
662                     case ',' :
663                             if (genericBalance == 0) return -1;
664                             break;
665                         case '>':
666                             if (genericBalance == 0) return -1;
667                             genericBalance--;
668                         break;
669                         case '[':
670                             if (genericBalance == 0) {
671                                 return pos;
672                             }
673                 }
674                 pos++;
675     }
676     return -1;
677 }
678 private static int checkNextChar(char[] typeName, char expectedChar, int pos, int length, boolean isOptional) {
679     pos = consumeWhitespace(typeName, pos, length);
680     if (pos < length && typeName[pos] == expectedChar) 
681         return pos + 1;
682     if (!isOptional) throw new IllegalArgumentException(new String(typeName));
683     return -1;
684 }
685
686 private static int encodeTypeSignature(char[] typeName, int start, boolean isResolved, int length, StringBuffer buffer) {
687     int pos = start;
688     pos = consumeWhitespace(typeName, pos, length);
689     if (pos >= length) throw new IllegalArgumentException(new String(typeName));
690     int checkPos;
691     char currentChar = typeName[pos];
692     switch (currentChar) {
693                 // primitive type?
694                 case 'b' :
695                     checkPos = checkName(BOOLEAN, typeName, pos, length);
696                     if (checkPos > 0) {
697                         pos = encodeArrayDimension(typeName, checkPos, length, buffer);
698                             buffer.append(C_BOOLEAN);
699                             return pos;
700                         } 
701                     checkPos = checkName(BYTE, typeName, pos, length);
702                     if (checkPos > 0) {
703                         pos = encodeArrayDimension(typeName, checkPos, length, buffer);
704                             buffer.append(C_BYTE);
705                             return pos;
706                         }
707                     break;
708                 case 'c':
709                     checkPos = checkName(CHAR, typeName, pos, length);
710                     if (checkPos > 0) {
711                         pos = encodeArrayDimension(typeName, checkPos, length, buffer);
712                             buffer.append(C_CHAR);
713                             return pos;
714                         } 
715                     break;
716                 case 'd':
717                     checkPos = checkName(DOUBLE, typeName, pos, length);
718                     if (checkPos > 0) {
719                         pos = encodeArrayDimension(typeName, checkPos, length, buffer);
720                             buffer.append(C_DOUBLE);
721                             return pos;
722                         } 
723                     break;
724                 case 'f':
725                     checkPos = checkName(FLOAT, typeName, pos, length);
726                     if (checkPos > 0) {
727                         pos = encodeArrayDimension(typeName, checkPos, length, buffer);
728                             buffer.append(C_FLOAT);
729                             return pos;
730                         } 
731                     break;
732                 case 'i':
733                     checkPos = checkName(INT, typeName, pos, length);
734                     if (checkPos > 0) {
735                         pos = encodeArrayDimension(typeName, checkPos, length, buffer);
736                             buffer.append(C_INT);
737                             return pos;
738                         } 
739                     break;
740                 case 'l':
741                     checkPos = checkName(LONG, typeName, pos, length);
742                     if (checkPos > 0) {
743                         pos = encodeArrayDimension(typeName, checkPos, length, buffer);
744                             buffer.append(C_LONG);
745                             return pos;
746                         } 
747                     break;
748                 case 's':
749                     checkPos = checkName(SHORT, typeName, pos, length);
750                     if (checkPos > 0) {
751                         pos = encodeArrayDimension(typeName, checkPos, length, buffer);
752                             buffer.append(C_SHORT);
753                             return pos;
754                         } 
755                     break;
756                 case 'v':
757                     checkPos = checkName(VOID, typeName, pos, length);
758                     if (checkPos > 0) {
759                         pos = encodeArrayDimension(typeName, checkPos, length, buffer);
760                             buffer.append(C_VOID);
761                             return pos;
762                         }
763                     break;
764                 case '?':
765                         // wildcard
766                         pos = consumeWhitespace(typeName, pos+1, length);
767                         checkPos = checkName(EXTENDS, typeName, pos, length);
768                         if (checkPos > 0) {
769                                 buffer.append(C_EXTENDS);
770                                 pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer);
771                                 return pos;
772                         }
773                         checkPos = checkName(SUPER, typeName, pos, length);
774                         if (checkPos > 0) {
775                                 buffer.append(C_SUPER);
776                                 pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer);
777                                 return pos;
778                         }
779                         buffer.append(C_STAR);
780                         return pos;
781     }               
782     // non primitive type
783     checkPos = checkArrayDimension(typeName, pos, length);
784         int end;
785         if (checkPos > 0) {
786             end = encodeArrayDimension(typeName, checkPos, length, buffer);
787         } else {
788             end = -1;
789         }
790         buffer.append(isResolved ? C_RESOLVED : C_UNRESOLVED);
791         while (true) { // loop on qualifiedName[<args>][.qualifiedName[<args>]*
792             pos = encodeQualifiedName(typeName, pos, length, buffer);
793                 checkPos = checkNextChar(typeName, '<', pos, length, true);
794                 if (checkPos > 0) {
795                         buffer.append(C_GENERIC_START);
796                     pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer);
797                     while ((checkPos = checkNextChar(typeName, ',', pos, length, true)) > 0) {
798                             pos = encodeTypeSignature(typeName, checkPos, isResolved, length, buffer);
799                     }
800                     pos = checkNextChar(typeName, '>', pos, length, false);
801                         buffer.append(C_GENERIC_END);
802                 }
803                 checkPos = checkNextChar(typeName, '.', pos, length, true);
804                 if (checkPos > 0) {
805                         buffer.append(C_DOT);
806                         pos = checkPos;
807                 } else {
808                         break;
809                 }
810         }
811         buffer.append(C_NAME_END);
812         if (end > 0) pos = end; // skip array dimension which were preprocessed
813     return pos;
814 }
815
816 /**
817  * Creates a new type signature from the given type name. If the type name is qualified,
818  * then it is expected to be dot-based. The type name may contain primitive
819  * types or array types. However, parameterized types are not supported.
820  * <p>
821  * For example:
822  * <pre>
823  * <code>
824  * createTypeSignature("int", hucairz) -> "I"
825  * createTypeSignature("java.lang.String", true) -> "Ljava.lang.String;"
826  * createTypeSignature("String", false) -> "QString;"
827  * createTypeSignature("java.lang.String", false) -> "Qjava.lang.String;"
828  * createTypeSignature("int []", false) -> "[I"
829  * </code>
830  * </pre>
831  * </p>
832  *
833  * @param typeName the possibly qualified type name
834  * @param isResolved <code>true</code> if the type name is to be considered
835  *   resolved (for example, a type name from a binary class file), and 
836  *   <code>false</code> if the type name is to be considered unresolved
837  *   (for example, a type name found in source code)
838  * @return the encoded type signature
839  */
840 public static String createTypeSignature(String typeName, boolean isResolved) {
841         return createTypeSignature(typeName == null ? null : typeName.toCharArray(), isResolved);
842 }
843
844 /**
845  * Returns the array count (array nesting depth) of the given type signature.
846  *
847  * @param typeSignature the type signature
848  * @return the array nesting depth, or 0 if not an array
849  * @exception IllegalArgumentException if the signature is not syntactically
850  *   correct
851  * 
852  * @since 2.0
853  */
854 public static int getArrayCount(char[] typeSignature) throws IllegalArgumentException { 
855         try {
856                 int count = 0;
857                 while (typeSignature[count] == C_ARRAY) {
858                         ++count;
859                 }
860                 return count;
861         } catch (ArrayIndexOutOfBoundsException e) { // signature is syntactically incorrect if last character is C_ARRAY
862                 throw new IllegalArgumentException();
863         }
864 }
865 /**
866  * Returns the array count (array nesting depth) of the given type signature.
867  *
868  * @param typeSignature the type signature
869  * @return the array nesting depth, or 0 if not an array
870  * @exception IllegalArgumentException if the signature is not syntactically
871  *   correct
872  */
873 public static int getArrayCount(String typeSignature) throws IllegalArgumentException {
874         return getArrayCount(typeSignature.toCharArray());
875 }
876 /**
877  * Returns the type signature without any array nesting.
878  * <p>
879  * For example:
880  * <pre>
881  * <code>
882  * getElementType({'[', '[', 'I'}) --> {'I'}.
883  * </code>
884  * </pre>
885  * </p>
886  * 
887  * @param typeSignature the type signature
888  * @return the type signature without arrays
889  * @exception IllegalArgumentException if the signature is not syntactically
890  *   correct
891  * 
892  * @since 2.0
893  */
894 public static char[] getElementType(char[] typeSignature) throws IllegalArgumentException {
895         int count = getArrayCount(typeSignature);
896         if (count == 0) return typeSignature;
897         int length = typeSignature.length;
898         char[] result = new char[length-count];
899         System.arraycopy(typeSignature, count, result, 0, length-count);
900         return result;
901 }
902 /**
903  * Returns the type signature without any array nesting.
904  * <p>
905  * For example:
906  * <pre>
907  * <code>
908  * getElementType("[[I") --> "I".
909  * </code>
910  * </pre>
911  * </p>
912  * 
913  * @param typeSignature the type signature
914  * @return the type signature without arrays
915  * @exception IllegalArgumentException if the signature is not syntactically
916  *   correct
917  */
918 public static String getElementType(String typeSignature) throws IllegalArgumentException {
919         return new String(getElementType(typeSignature.toCharArray()));
920 }
921 /**
922  * Returns the number of parameter types in the given method signature.
923  *
924  * @param methodSignature the method signature
925  * @return the number of parameters
926  * @exception IllegalArgumentException if the signature is not syntactically
927  *   correct
928  * @since 2.0
929  */
930 public static int getParameterCount(char[] methodSignature) throws IllegalArgumentException {
931         try {
932                 int count = 0;
933                 int i = CharOperation.indexOf(C_PARAM_START, methodSignature);
934                 if (i < 0) {
935                         throw new IllegalArgumentException();
936                 } else {
937                         i++;
938                 }
939                 for (;;) {
940                         if (methodSignature[i] == C_PARAM_END) {
941                                 return count;
942                         }
943                         int e= scanTypeSignature(methodSignature, i);
944                         if (e < 0) {
945                                 throw new IllegalArgumentException();
946                         } else {
947                                 i = e + 1;
948                         }
949                         count++;
950                 }
951         } catch (ArrayIndexOutOfBoundsException e) {
952                 throw new IllegalArgumentException();
953         }
954 }
955
956 /**
957  * Returns the kind of type signature encoded by the given string.
958  * 
959  * @param typeSignature the type signature string
960  * @return the kind of type signature; one of the kind constants:
961  * {@link #ARRAY_TYPE_SIGNATURE}, {@link #CLASS_TYPE_SIGNATURE},
962  * {@link #BASE_TYPE_SIGNATURE}, or {@link #TYPE_VARIABLE_SIGNATURE}
963  * @exception IllegalArgumentException if this is not a type signature
964  * @since 3.0
965  */
966 public static int getTypeSignatureKind(char[] typeSignature) {
967         // need a minimum 1 char
968         if (typeSignature.length < 1) {
969                 throw new IllegalArgumentException();
970         }
971         char c = typeSignature[0];
972         switch (c) {
973                 case C_ARRAY :
974                         return ARRAY_TYPE_SIGNATURE;
975                 case C_RESOLVED :
976                 case C_UNRESOLVED :
977                         return CLASS_TYPE_SIGNATURE;
978                 case C_TYPE_VARIABLE :
979                         return TYPE_VARIABLE_SIGNATURE;
980                 case C_BOOLEAN :
981                 case C_BYTE :
982                 case C_CHAR :
983                 case C_DOUBLE :
984                 case C_FLOAT :
985                 case C_INT :
986                 case C_LONG :
987                 case C_SHORT :
988                 case C_VOID :
989                         return BASE_TYPE_SIGNATURE;
990                 default :
991                         throw new IllegalArgumentException();
992         }
993 }
994
995 /**
996  * Returns the kind of type signature encoded by the given string.
997  * 
998  * @param typeSignature the type signature string
999  * @return the kind of type signature; one of the kind constants:
1000  * {@link #ARRAY_TYPE_SIGNATURE}, {@link #CLASS_TYPE_SIGNATURE},
1001  * {@link #BASE_TYPE_SIGNATURE}, or {@link #TYPE_VARIABLE_SIGNATURE}
1002  * @exception IllegalArgumentException if this is not a type signature
1003  * @since 3.0
1004  */
1005 public static int getTypeSignatureKind(String typeSignature) {
1006         // need a minimum 1 char
1007         if (typeSignature.length() < 1) {
1008                 throw new IllegalArgumentException();
1009         }
1010         char c = typeSignature.charAt(0);
1011         switch (c) {
1012                 case C_ARRAY :
1013                         return ARRAY_TYPE_SIGNATURE;
1014                 case C_RESOLVED :
1015                 case C_UNRESOLVED :
1016                         return CLASS_TYPE_SIGNATURE;
1017                 case C_TYPE_VARIABLE :
1018                         return TYPE_VARIABLE_SIGNATURE;
1019                 case C_BOOLEAN :
1020                 case C_BYTE :
1021                 case C_CHAR :
1022                 case C_DOUBLE :
1023                 case C_FLOAT :
1024                 case C_INT :
1025                 case C_LONG :
1026                 case C_SHORT :
1027                 case C_VOID :
1028                         return BASE_TYPE_SIGNATURE;
1029                 default :
1030                         throw new IllegalArgumentException();
1031         }
1032 }
1033
1034 /**
1035  * Scans the given string for a type signature starting at the given index
1036  * and returns the index of the last character.
1037  * <pre>
1038  * TypeSignature:
1039  *  |  BaseTypeSignature
1040  *  |  ArrayTypeSignature
1041  *  |  ClassTypeSignature
1042  *  |  TypeVariableSignature
1043  * </pre>
1044  * 
1045  * @param string the signature string
1046  * @param start the 0-based character index of the first character
1047  * @return the 0-based character index of the last character
1048  * @exception IllegalArgumentException if this is not a type signature
1049  * @see #appendTypeSignature(char[], int, boolean, StringBuffer)
1050  */
1051 private static int scanTypeSignature(char[] string, int start) {
1052         // need a minimum 1 char
1053         if (start >= string.length) {
1054                 throw new IllegalArgumentException();
1055         }
1056         char c = string[start];
1057         switch (c) {
1058                 case C_ARRAY :
1059                         return scanArrayTypeSignature(string, start);
1060                 case C_RESOLVED :
1061                 case C_UNRESOLVED :
1062                         return scanClassTypeSignature(string, start);
1063                 case C_TYPE_VARIABLE :
1064                         return scanTypeVariableSignature(string, start);
1065                 case C_BOOLEAN :
1066                 case C_BYTE :
1067                 case C_CHAR :
1068                 case C_DOUBLE :
1069                 case C_FLOAT :
1070                 case C_INT :
1071                 case C_LONG :
1072                 case C_SHORT :
1073                 case C_VOID :
1074                         return scanBaseTypeSignature(string, start);
1075                 default :
1076                         throw new IllegalArgumentException();
1077         }
1078 }
1079
1080 /**
1081  * Scans the given string for a base type signature starting at the given index
1082  * and returns the index of the last character.
1083  * <pre>
1084  * BaseTypeSignature:
1085  *     <b>B</b> | <b>C</b> | <b>D</b> | <b>F</b> | <b>I</b>
1086  *   | <b>J</b> | <b>S</b> | <b>V</b> | <b>Z</b>
1087  * </pre>
1088  * Note that although the base type "V" is only allowed in method return types,
1089  * there is no syntactic ambiguity. This method will accept them anywhere
1090  * without complaint.
1091  * 
1092  * @param string the signature string
1093  * @param start the 0-based character index of the first character
1094  * @return the 0-based character index of the last character
1095  * @exception IllegalArgumentException if this is not a base type signature
1096  */
1097 private static int scanBaseTypeSignature(char[] string, int start) {
1098         // need a minimum 1 char
1099         if (start >= string.length) {
1100                 throw new IllegalArgumentException();
1101         }
1102         char c = string[start];
1103         if ("BCDFIJSVZ".indexOf(c) >= 0) { //$NON-NLS-1$
1104                 return start;
1105         } else {
1106                 throw new IllegalArgumentException();
1107         }
1108 }
1109
1110 /**
1111  * Scans the given string for an array type signature starting at the given
1112  * index and returns the index of the last character.
1113  * <pre>
1114  * ArrayTypeSignature:
1115  *     <b>[</b> TypeSignature
1116  * </pre>
1117  * 
1118  * @param string the signature string
1119  * @param start the 0-based character index of the first character
1120  * @return the 0-based character index of the last character
1121  * @exception IllegalArgumentException if this is not an array type signature
1122  * @see #appendArrayTypeSignature(char[], int, boolean, StringBuffer)
1123  */
1124 private static int scanArrayTypeSignature(char[] string, int start) {
1125         // need a minimum 2 char
1126         if (start >= string.length - 1) {
1127                 throw new IllegalArgumentException();
1128         }
1129         char c = string[start];
1130         if (c != C_ARRAY) { //$NON-NLS-1$
1131                 throw new IllegalArgumentException();
1132         }
1133         return scanTypeSignature(string, start + 1);
1134 }
1135
1136 /**
1137  * Scans the given string for a type variable signature starting at the given
1138  * index and returns the index of the last character.
1139  * <pre>
1140  * TypeVariableSignature:
1141  *     <b>T</b> Identifier <b>;</b>
1142  * </pre>
1143  * 
1144  * @param string the signature string
1145  * @param start the 0-based character index of the first character
1146  * @return the 0-based character index of the last character
1147  * @exception IllegalArgumentException if this is not a type variable signature
1148  */
1149 private static int scanTypeVariableSignature(char[] string, int start) {
1150         // need a minimum 3 chars "Tx;"
1151         if (start >= string.length - 2) { 
1152                 throw new IllegalArgumentException();
1153         }
1154         // must start in "T"
1155         char c = string[start];
1156         if (c != C_TYPE_VARIABLE) {
1157                 throw new IllegalArgumentException();
1158         }
1159         int id = scanIdentifier(string, start + 1);
1160         c = string[id + 1];
1161         if (c == C_SEMICOLON) {
1162                 return id + 1;
1163         } else {
1164                 throw new IllegalArgumentException();
1165         }
1166 }
1167
1168 /**
1169  * Scans the given string for an identifier starting at the given
1170  * index and returns the index of the last character. 
1171  * Stop characters are: ";", ":", "&lt;", "&gt;", "/", ".".
1172  * 
1173  * @param string the signature string
1174  * @param start the 0-based character index of the first character
1175  * @return the 0-based character index of the last character
1176  * @exception IllegalArgumentException if this is not an identifier
1177  */
1178 private static int scanIdentifier(char[] string, int start) {
1179         // need a minimum 1 char
1180         if (start >= string.length) { 
1181                 throw new IllegalArgumentException();
1182         }
1183         int p = start;
1184         while (true) {
1185                 char c = string[p];
1186                 if (c == '<' || c == '>' || c == ':' || c == ';' || c == '.' || c == '/') {
1187                         return p - 1;
1188                 }
1189                 p++;
1190                 if (p == string.length) {
1191                         return p - 1;
1192                 }
1193         }
1194 }
1195
1196 /**
1197  * Scans the given string for a class type signature starting at the given
1198  * index and returns the index of the last character.
1199  * <pre>
1200  * ClassTypeSignature:
1201  *     { <b>L</b> | <b>Q</b> } Identifier
1202  *           { { <b>/</b> | <b>.</b> Identifier [ <b>&lt;</b> TypeArgumentSignature* <b>&gt;</b> ] }
1203  *           <b>;</b>
1204  * </pre>
1205  * Note that although all "/"-identifiers most come before "."-identifiers,
1206  * there is no syntactic ambiguity. This method will accept them without
1207  * complaint.
1208  * 
1209  * @param string the signature string
1210  * @param start the 0-based character index of the first character
1211  * @return the 0-based character index of the last character
1212  * @exception IllegalArgumentException if this is not a class type signature
1213  * @see #appendClassTypeSignature(char[], int, boolean, StringBuffer)
1214  */
1215 private static int scanClassTypeSignature(char[] string, int start) {
1216         // need a minimum 3 chars "Lx;"
1217         if (start >= string.length - 2) { 
1218                 throw new IllegalArgumentException();
1219         }
1220         // must start in "L" or "Q"
1221         char c = string[start];
1222         if (c != C_RESOLVED && c != C_UNRESOLVED) {
1223                 return -1;
1224         }
1225         int p = start + 1;
1226         while (true) {
1227                 if (p >= string.length) {
1228                         throw new IllegalArgumentException();
1229                 }
1230                 c = string[p];
1231                 if (c == C_SEMICOLON) {
1232                         // all done
1233                         return p;
1234                 } else if (c == C_GENERIC_START) {
1235                         int e = scanTypeArgumentSignatures(string, p);
1236                         p = e;
1237                 } else if (c == C_DOT || c == '/') {
1238                         int id = scanIdentifier(string, p + 1);
1239                         p = id;
1240                 }
1241                 p++;
1242         }
1243 }
1244
1245 /**
1246  * Scans the given string for a list of type argument signatures starting at
1247  * the given index and returns the index of the last character.
1248  * <pre>
1249  * TypeArgumentSignatures:
1250  *     <b>&lt;</b> TypeArgumentSignature* <b>&gt;</b>
1251  * </pre>
1252  * Note that although there is supposed to be at least one type argument, there
1253  * is no syntactic ambiguity if there are none. This method will accept zero
1254  * type argument signatures without complaint.
1255  * 
1256  * @param string the signature string
1257  * @param start the 0-based character index of the first character
1258  * @return the 0-based character index of the last character
1259  * @exception IllegalArgumentException if this is not a list of type arguments
1260  * signatures
1261  * @see #appendTypeArgumentSignatures(char[], int, boolean, StringBuffer)
1262  */
1263 private static int scanTypeArgumentSignatures(char[] string, int start) {
1264         // need a minimum 2 char "<>"
1265         if (start >= string.length - 1) {
1266                 throw new IllegalArgumentException();
1267         }
1268         char c = string[start];
1269         if (c != C_GENERIC_START) {
1270                 throw new IllegalArgumentException();
1271         }
1272         int p = start + 1;
1273         while (true) {
1274                 if (p >= string.length) {
1275                         throw new IllegalArgumentException();
1276                 }
1277                 c = string[p];
1278                 if (c == C_GENERIC_END) {
1279                         return p;
1280                 }
1281                 int e = scanTypeArgumentSignature(string, p);
1282                 p = e + 1;
1283         }
1284 }
1285
1286 /**
1287  * Scans the given string for a type argument signature starting at the given
1288  * index and returns the index of the last character.
1289  * <pre>
1290  * TypeArgumentSignature:
1291  *     <b>&#42;</b>
1292  *  |  <b>+</b> TypeSignature
1293  *  |  <b>-</b> TypeSignature
1294  *  |  TypeSignature
1295  * </pre>
1296  * Note that although base types are not allowed in type arguments, there is
1297  * no syntactic ambiguity. This method will accept them without complaint.
1298  * 
1299  * @param string the signature string
1300  * @param start the 0-based character index of the first character
1301  * @return the 0-based character index of the last character
1302  * @exception IllegalArgumentException if this is not a type argument signature
1303  * @see #appendTypeArgumentSignature(char[], int, boolean, StringBuffer)
1304  */
1305 private static int scanTypeArgumentSignature(char[] string, int start) {
1306         // need a minimum 1 char
1307         if (start >= string.length) {
1308                 throw new IllegalArgumentException();
1309         }
1310         char c = string[start];
1311         if (c == C_STAR) {
1312                 return start;
1313         }
1314         if (c == '+' || c == '-') {
1315                 return scanTypeSignature(string, start + 1);
1316         } else {
1317                 return scanTypeSignature(string, start);
1318         }
1319 }
1320
1321 /**
1322  * Returns the number of parameter types in the given method signature.
1323  *
1324  * @param methodSignature the method signature
1325  * @return the number of parameters
1326  * @exception IllegalArgumentException if the signature is not syntactically
1327  *   correct
1328  */
1329 public static int getParameterCount(String methodSignature) throws IllegalArgumentException {
1330         return getParameterCount(methodSignature.toCharArray());
1331 }
1332
1333 /**
1334  * Extracts the parameter type signatures from the given method signature. 
1335  * The method signature is expected to be dot-based.
1336  *
1337  * @param methodSignature the method signature
1338  * @return the list of parameter type signatures
1339  * @exception IllegalArgumentException if the signature is syntactically
1340  *   incorrect
1341  * 
1342  * @since 2.0
1343  */
1344 public static char[][] getParameterTypes(char[] methodSignature) throws IllegalArgumentException {
1345         try {
1346                 int count = getParameterCount(methodSignature);
1347                 char[][] result = new char[count][];
1348                 if (count == 0) {
1349                         return result;
1350                 }
1351                 int i = CharOperation.indexOf(C_PARAM_START, methodSignature);
1352                 if (i < 0) {
1353                         throw new IllegalArgumentException();
1354                 } else {
1355                         i++;
1356                 }
1357                 int t = 0;
1358                 for (;;) {
1359                         if (methodSignature[i] == C_PARAM_END) {
1360                                 return result;
1361                         }
1362                         int e = scanTypeSignature(methodSignature, i);
1363                         if (e < 0) {
1364                                 throw new IllegalArgumentException();
1365                         }
1366                         result[t] = CharOperation.subarray(methodSignature, i, e + 1);
1367                         t++;
1368                         i = e + 1;
1369                 }
1370         } catch (ArrayIndexOutOfBoundsException e) {
1371                 throw new IllegalArgumentException();
1372         }
1373 }
1374
1375 /**
1376  * Extracts the parameter type signatures from the given method signature. 
1377  * The method signature is expected to be dot-based.
1378  *
1379  * @param methodSignature the method signature
1380  * @return the list of parameter type signatures
1381  * @exception IllegalArgumentException if the signature is syntactically
1382  *   incorrect
1383  */
1384 public static String[] getParameterTypes(String methodSignature) throws IllegalArgumentException {
1385         char[][] parameterTypes = getParameterTypes(methodSignature.toCharArray());
1386         return CharOperation.toStrings(parameterTypes);
1387 }
1388
1389 /**
1390  * Extracts the thrown exception type signatures from the given method signature if any
1391  * The method signature is expected to be dot-based.
1392  *
1393  * @param methodSignature the method signature
1394  * @return the list of thrown exception type signatures
1395  * @exception IllegalArgumentException if the signature is syntactically
1396  *   incorrect
1397  */
1398 public static String[] getThrownExceptionTypes(String methodSignature) throws IllegalArgumentException {
1399         char[][] parameterTypes = getThrownExceptionTypes(methodSignature.toCharArray());
1400         return CharOperation.toStrings(parameterTypes);
1401 }
1402
1403 /**
1404  * Extracts the thrown exception type signatures from the given method signature if any
1405  * The method signature is expected to be dot-based.
1406  *
1407  * @param methodSignature the method signature
1408  * @return the list of thrown exception type signatures
1409  * @exception IllegalArgumentException if the signature is syntactically
1410  *   incorrect
1411  */
1412 public static char[][] getThrownExceptionTypes(char[] methodSignature) throws IllegalArgumentException {
1413         // skip type parameters
1414         int paren = CharOperation.lastIndexOf(C_PARAM_END, methodSignature);
1415         if (paren == -1) {
1416                 throw new IllegalArgumentException();
1417         }
1418         // ignore return type
1419         int exceptionStart = scanTypeSignature(methodSignature, paren+1) + 1;
1420         int length = methodSignature.length;
1421         if (exceptionStart == length) return CharOperation.NO_CHAR_CHAR;
1422         
1423         ArrayList exceptionList = new ArrayList(1);
1424         int i = exceptionStart;
1425         while (i < length) {
1426                 i = scanTypeSignature(methodSignature, i) + 1;
1427                 exceptionList.add(CharOperation.subarray(methodSignature, exceptionStart,i));   
1428                 exceptionStart = i;
1429         }
1430         char[][] result;
1431         exceptionList.toArray(result = new char[exceptionList.size()][]);
1432         return result;
1433 }
1434
1435 /**
1436  * Extracts the type argument signatures from the given type signature.
1437  * Returns an empty array if the type signature is not a parameterized type signature.
1438  *
1439  * @param parameterizedTypeSignature the parameterized type signature
1440  * @return the signatures of the type arguments
1441  * @exception IllegalArgumentException if the signature is syntactically incorrect
1442  * 
1443  * @since 3.1
1444  */
1445 public static char[][] getTypeArguments(char[] parameterizedTypeSignature) throws IllegalArgumentException {
1446         int length = parameterizedTypeSignature.length;
1447         if (length < 2 || parameterizedTypeSignature[length-2] != C_GENERIC_END)
1448                 // cannot have type arguments otherwise signature would end by ">;"
1449                 return CharOperation.NO_CHAR_CHAR;
1450         int count = 1; // start to count generic end/start peers
1451         int start = length - 2;
1452         while (start >= 0 && count > 0) {
1453                 switch (parameterizedTypeSignature[--start]) {
1454                         case C_GENERIC_START:
1455                                 count--;
1456                                 break;
1457                         case C_GENERIC_END:
1458                                 count++;
1459                                 break;
1460                 }
1461         }
1462         if (start < 0) // invalid number of generic start/end
1463                 throw new IllegalArgumentException();
1464         ArrayList args = new ArrayList();
1465         int p = start + 1;
1466         while (true) {
1467                 if (p >= parameterizedTypeSignature.length) {
1468                         throw new IllegalArgumentException();
1469                 }
1470                 char c = parameterizedTypeSignature[p];
1471                 if (c == C_GENERIC_END) {
1472                         int size = args.size();
1473                         char[][] result = new char[size][];
1474                         args.toArray(result);
1475                         return result;
1476                 }
1477                 int e = scanTypeArgumentSignature(parameterizedTypeSignature, p);
1478                 args.add(CharOperation.subarray(parameterizedTypeSignature, p, e+1));
1479                 p = e + 1;
1480         }
1481 }
1482
1483 /**
1484  * Extracts the type argument signatures from the given type signature.
1485  * Returns an empty array if the type signature is not a parameterized type signature.
1486  *
1487  * @param parameterizedTypeSignature the parameterized type signature
1488  * @return the signatures of the type arguments
1489  * @exception IllegalArgumentException if the signature is syntactically incorrect
1490  * 
1491  * @since 3.1
1492  */
1493 public static String[] getTypeArguments(String parameterizedTypeSignature) throws IllegalArgumentException {
1494         char[][] args = getTypeArguments(parameterizedTypeSignature.toCharArray());
1495         return CharOperation.toStrings(args);
1496 }
1497
1498 /**
1499  * Extracts the type erasure signature from the given parameterized type signature.
1500  * Returns the given type signature if it is not parameterized.
1501  * 
1502  * @param parameterizedTypeSignature the parameterized type signature
1503  * @return the signature of the type erasure
1504  * @exception IllegalArgumentException if the signature is syntactically
1505  *   incorrect
1506  * 
1507  * @since 3.1
1508  */
1509 public static char[] getTypeErasure(char[] parameterizedTypeSignature) throws IllegalArgumentException {
1510         int end = CharOperation.indexOf(C_GENERIC_START, parameterizedTypeSignature);
1511         if (end == -1) return parameterizedTypeSignature;
1512         int length = parameterizedTypeSignature.length;
1513         char[] result = new char[length];
1514         int pos = 0;
1515         int start = 0;
1516         int deep= 0;
1517         for (int idx=end; idx<length; idx++) {
1518                 switch (parameterizedTypeSignature[idx]) {
1519                         case C_GENERIC_START:
1520                                 if (deep == 0) {
1521                                         int size = idx-start;
1522                                         System.arraycopy(parameterizedTypeSignature, start, result, pos, size);
1523                                         end = idx;
1524                                         pos += size;
1525                                 }
1526                                 deep++;
1527                                 break;
1528                         case C_GENERIC_END:
1529                                 deep--;
1530                                 if (deep < 0) throw new IllegalArgumentException();
1531                                 if (deep == 0) start = idx+1;
1532                                 break;
1533                 }
1534         }
1535         if (deep > 0) throw new IllegalArgumentException();
1536         int size = pos+length-start;
1537         char[] resized = new char[size];
1538         System.arraycopy(result, 0, resized, 0, pos);
1539         System.arraycopy(parameterizedTypeSignature, start, resized, pos, length-start);
1540         return resized;
1541 }
1542
1543 /**
1544  * Extracts the type erasure signature from the given parameterized type signature.
1545  * Returns the given type signature if it is not parameterized.
1546  * 
1547  * @param parameterizedTypeSignature the parameterized type signature
1548  * @return the signature of the type erasure
1549  * @exception IllegalArgumentException if the signature is syntactically
1550  *   incorrect
1551  * 
1552  * @since 3.1
1553  */
1554 public static String getTypeErasure(String parameterizedTypeSignature) throws IllegalArgumentException {
1555         return new String(getTypeErasure(parameterizedTypeSignature.toCharArray()));
1556 }
1557
1558 /**
1559  * Extracts the type parameter signatures from the given method or type signature. 
1560  * The method or type signature is expected to be dot-based.
1561  *
1562  * @param methodOrTypeSignature the method or type signature
1563  * @return the list of type parameter signatures
1564  * @exception IllegalArgumentException if the signature is syntactically
1565  *   incorrect
1566  * 
1567  * @since 3.1
1568  */
1569 public static char[][] getTypeParameters(char[] methodOrTypeSignature) throws IllegalArgumentException {
1570         try {
1571                 int length = methodOrTypeSignature.length;
1572                 if (length == 0) return CharOperation.NO_CHAR_CHAR;
1573                 if (methodOrTypeSignature[0] != C_GENERIC_START) return CharOperation.NO_CHAR_CHAR;
1574                 
1575                 ArrayList paramList = new ArrayList(1);
1576                 int paramStart = 1, i = 1;  // start after leading '<'
1577                 while (i < length) {
1578                         if (methodOrTypeSignature[i] == C_GENERIC_END) {
1579                                 int size = paramList.size();
1580                                 if (size == 0) throw new IllegalArgumentException(); 
1581                                 char[][] result;
1582                                 paramList.toArray(result = new char[size][]);
1583                                 return result;
1584                         }
1585                         i = CharOperation.indexOf(C_COLON, methodOrTypeSignature, i);
1586                         if (i < 0 || i >= length) throw new IllegalArgumentException();
1587                         // iterate over bounds
1588                         nextBound: while (methodOrTypeSignature[i] == ':') {
1589                                 i++; // skip colon
1590                                 if (methodOrTypeSignature[i] == ':') {
1591                                         continue nextBound; // empty bound
1592                                 }
1593                                 i = scanTypeSignature(methodOrTypeSignature, i);
1594                                 i++; // position at start of next param if any
1595                         }
1596                         paramList.add(CharOperation.subarray(methodOrTypeSignature, paramStart, i));
1597                         paramStart = i; // next param start from here
1598                 }
1599         } catch (ArrayIndexOutOfBoundsException e) {
1600                 // invalid signature, fall through
1601         }
1602         throw new IllegalArgumentException();
1603 }
1604 /**
1605  * Extracts the type parameter signatures from the given method or type signature. 
1606  * The method or type signature is expected to be dot-based.
1607  *
1608  * @param methodOrTypeSignature the method or type signature
1609  * @return the list of type parameter signatures
1610  * @exception IllegalArgumentException if the signature is syntactically
1611  *   incorrect
1612  * 
1613  * @since 3.1
1614  */
1615 public static String[] getTypeParameters(String methodOrTypeSignature) throws IllegalArgumentException {
1616         char[][] params = getTypeParameters(methodOrTypeSignature.toCharArray());
1617         return CharOperation.toStrings(params);
1618 }
1619
1620 /**
1621  * Extracts the type variable name from the given formal type parameter
1622  * signature. The signature is expected to be dot-based.
1623  *
1624  * @param formalTypeParameterSignature the formal type parameter signature
1625  * @return the name of the type variable
1626  * @exception IllegalArgumentException if the signature is syntactically
1627  *   incorrect
1628  * @since 3.0
1629  */
1630 public static String getTypeVariable(String formalTypeParameterSignature) throws IllegalArgumentException {
1631         return new String(getTypeVariable(formalTypeParameterSignature.toCharArray()));
1632 }
1633
1634 /**
1635  * Extracts the type variable name from the given formal type parameter
1636  * signature. The signature is expected to be dot-based.
1637  *
1638  * @param formalTypeParameterSignature the formal type parameter signature
1639  * @return the name of the type variable
1640  * @exception IllegalArgumentException if the signature is syntactically
1641  *   incorrect
1642  * @since 3.0
1643  */
1644 public static char[] getTypeVariable(char[] formalTypeParameterSignature) throws IllegalArgumentException {
1645         int p = CharOperation.indexOf(C_COLON, formalTypeParameterSignature);
1646         if (p < 0) {
1647                 // no ":" means can't be a formal type parameter signature
1648                 throw new IllegalArgumentException();
1649         }
1650         return CharOperation.subarray(formalTypeParameterSignature, 0, p);
1651 }
1652
1653 /**
1654  * Extracts the class and interface bounds from the given formal type
1655  * parameter signature. The class bound, if present, is listed before
1656  * the interface bounds. The signature is expected to be dot-based.
1657  *
1658  * @param formalTypeParameterSignature the formal type parameter signature
1659  * @return the (possibly empty) list of type signatures for the bounds
1660  * @exception IllegalArgumentException if the signature is syntactically
1661  *   incorrect
1662  * @since 3.0
1663  */
1664 public static char[][] getTypeParameterBounds(char[] formalTypeParameterSignature) throws IllegalArgumentException {
1665         int p1 = CharOperation.indexOf(C_COLON, formalTypeParameterSignature);
1666         if (p1 < 0) {
1667                 // no ":" means can't be a formal type parameter signature
1668                 throw new IllegalArgumentException();
1669         }
1670         if (p1 == formalTypeParameterSignature.length - 1) {
1671                 // no class or interface bounds
1672                 return CharOperation.NO_CHAR_CHAR;
1673         }
1674         int p2 = CharOperation.indexOf(C_COLON, formalTypeParameterSignature, p1 + 1);
1675         char[] classBound;
1676         if (p2 < 0) {
1677                 // no interface bounds
1678                 classBound = CharOperation.subarray(formalTypeParameterSignature, p1 + 1, formalTypeParameterSignature.length);
1679                 return new char[][] {classBound};
1680         }
1681         if (p2 == p1 + 1) {
1682                 // no class bound, but 1 or more interface bounds
1683                 classBound = null;
1684         } else {
1685                 classBound = CharOperation.subarray(formalTypeParameterSignature, p1 + 1, p2);
1686         }
1687         char[][] interfaceBounds = CharOperation.splitOn(C_COLON, formalTypeParameterSignature, p2 + 1, formalTypeParameterSignature.length);
1688         if (classBound == null) {
1689                 return interfaceBounds;
1690         }
1691         int resultLength = interfaceBounds.length + 1;
1692         char[][] result = new char[resultLength][];
1693         result[0] = classBound;
1694         System.arraycopy(interfaceBounds, 0, result, 1, interfaceBounds.length);
1695         return result;
1696 }
1697
1698 /**
1699  * Extracts the class and interface bounds from the given formal type
1700  * parameter signature. The class bound, if present, is listed before
1701  * the interface bounds. The signature is expected to be dot-based.
1702  *
1703  * @param formalTypeParameterSignature the formal type parameter signature
1704  * @return the (possibly empty) list of type signatures for the bounds
1705  * @exception IllegalArgumentException if the signature is syntactically
1706  *   incorrect
1707  * @since 3.0
1708  */
1709 public static String[] getTypeParameterBounds(String formalTypeParameterSignature) throws IllegalArgumentException {
1710         char[][] bounds = getTypeParameterBounds(formalTypeParameterSignature.toCharArray());
1711         return CharOperation.toStrings(bounds);
1712 }
1713
1714 /**
1715  * Returns a char array containing all but the last segment of the given 
1716  * dot-separated qualified name. Returns the empty char array if it is not qualified.
1717  * <p>
1718  * For example:
1719  * <pre>
1720  * <code>
1721  * getQualifier({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g'}
1722  * getQualifier({'O', 'u', 't', 'e', 'r', '.', 'I', 'n', 'n', 'e', 'r'}) -> {'O', 'u', 't', 'e', 'r'}
1723  * getQualifier({'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'L', 'i', 's', 't', '<', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', '>'}) -> {'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l'}
1724  * </code>
1725  * </pre>
1726  * </p>
1727  *
1728  * @param name the name
1729  * @return the qualifier prefix, or the empty char array if the name contains no
1730  *   dots
1731  * @exception NullPointerException if name is null
1732  * @since 2.0
1733  */
1734 public static char[] getQualifier(char[] name) {
1735         int firstGenericStart = CharOperation.indexOf(C_GENERIC_START, name);
1736         int lastDot = CharOperation.lastIndexOf(C_DOT, name, 0, firstGenericStart == -1 ? name.length-1 : firstGenericStart);
1737         if (lastDot == -1) {
1738                 return CharOperation.NO_CHAR;
1739         }
1740         return CharOperation.subarray(name, 0, lastDot);
1741 }
1742 /**
1743  * Returns a string containing all but the last segment of the given 
1744  * dot-separated qualified name. Returns the empty string if it is not qualified.
1745  * <p>
1746  * For example:
1747  * <pre>
1748  * <code>
1749  * getQualifier("java.lang.Object") -> "java.lang"
1750  * getQualifier("Outer.Inner") -> "Outer"
1751  * getQualifier("java.util.List<java.lang.String>") -> "java.util"
1752  * </code>
1753  * </pre>
1754  * </p>
1755  *
1756  * @param name the name
1757  * @return the qualifier prefix, or the empty string if the name contains no
1758  *   dots
1759  * @exception NullPointerException if name is null
1760  */
1761 public static String getQualifier(String name) {
1762         char[] qualifier = getQualifier(name.toCharArray());
1763         if (qualifier.length == 0) return EMPTY;
1764         return new String(qualifier);
1765 }
1766 /**
1767  * Extracts the return type from the given method signature. The method signature is 
1768  * expected to be dot-based.
1769  *
1770  * @param methodSignature the method signature
1771  * @return the type signature of the return type
1772  * @exception IllegalArgumentException if the signature is syntactically
1773  *   incorrect
1774  * 
1775  * @since 2.0
1776  */
1777 public static char[] getReturnType(char[] methodSignature) throws IllegalArgumentException {
1778         // skip type parameters
1779         int paren = CharOperation.lastIndexOf(C_PARAM_END, methodSignature);
1780         if (paren == -1) {
1781                 throw new IllegalArgumentException();
1782         }
1783         // there could be thrown exceptions behind, thus scan one type exactly
1784         int last = scanTypeSignature(methodSignature, paren+1);
1785         return CharOperation.subarray(methodSignature, paren + 1, last+1);
1786 }
1787 /**
1788  * Extracts the return type from the given method signature. The method signature is 
1789  * expected to be dot-based.
1790  *
1791  * @param methodSignature the method signature
1792  * @return the type signature of the return type
1793  * @exception IllegalArgumentException if the signature is syntactically
1794  *   incorrect
1795  */
1796 public static String getReturnType(String methodSignature) throws IllegalArgumentException {
1797         return new String(getReturnType(methodSignature.toCharArray()));
1798 }
1799 /**
1800  * Returns package fragment of a type signature. The package fragment separator must be '.'
1801  * and the type fragment separator must be '$'.
1802  * <p>
1803  * For example:
1804  * <pre>
1805  * <code>
1806  * getSignatureQualifier({'L', 'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'M', 'a', 'p', '$', 'E', 'n', 't', 'r', 'y', ';'}) -> {'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l'}
1807  * </code>
1808  * </pre>
1809  * </p>
1810  * 
1811  * @param typeSignature the type signature
1812  * @return the package fragment (separators are '.')
1813  * @since 3.1
1814  */
1815 public static char[] getSignatureQualifier(char[] typeSignature) {
1816         if(typeSignature == null) return CharOperation.NO_CHAR;
1817         
1818         char[] qualifiedType = Signature.toCharArray(typeSignature);
1819         
1820         int dotCount = 0;
1821         indexFound: for(int i = 0; i < typeSignature.length; i++) {
1822                 switch(typeSignature[i]) {
1823                         case C_DOT:
1824                                 dotCount++;
1825                                 break;
1826                         case C_GENERIC_START:
1827                                 break indexFound;
1828                         case C_DOLLAR:
1829                                 break indexFound;
1830                 }
1831         }
1832         
1833         if(dotCount > 0) {
1834                 for(int i = 0; i < qualifiedType.length; i++) {
1835                         if(qualifiedType[i] == '.') {
1836                                 dotCount--;
1837                         }
1838                         if(dotCount <= 0) {
1839                                 return CharOperation.subarray(qualifiedType, 0, i);
1840                         }
1841                 }
1842         }
1843         return CharOperation.NO_CHAR;
1844 }
1845 /**
1846  * Returns package fragment of a type signature. The package fragment separator must be '.'
1847  * and the type fragment separator must be '$'.
1848  * <p>
1849  * For example:
1850  * <pre>
1851  * <code>
1852  * getSignatureQualifier("Ljava.util.Map$Entry") -> "java.util"
1853  * </code>
1854  * </pre>
1855  * </p>
1856  * 
1857  * @param typeSignature the type signature
1858  * @return the package fragment (separators are '.')
1859  * @since 3.1
1860  */
1861 public static String getSignatureQualifier(String typeSignature) {
1862         return new String(getSignatureQualifier(typeSignature == null ? null : typeSignature.toCharArray()));
1863 }
1864 /**
1865  * Returns type fragment of a type signature. The package fragment separator must be '.'
1866  * and the type fragment separator must be '$'.
1867  * <p>
1868  * For example:
1869  * <pre>
1870  * <code>
1871  * getSignatureSimpleName({'L', 'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'M', 'a', 'p', '$', 'E', 'n', 't', 'r', 'y', ';'}) -> {'M', 'a', 'p', '.', 'E', 'n', 't', 'r', 'y'}
1872  * </code>
1873  * </pre>
1874  * </p>
1875  * 
1876  * @param typeSignature the type signature
1877  * @return the type fragment (separators are '.')
1878  * @since 3.1
1879  */
1880 public static char[] getSignatureSimpleName(char[] typeSignature) {
1881         if(typeSignature == null) return CharOperation.NO_CHAR;
1882         
1883         char[] qualifiedType = Signature.toCharArray(typeSignature);
1884         
1885         int dotCount = 0;
1886         indexFound: for(int i = 0; i < typeSignature.length; i++) {
1887                 switch(typeSignature[i]) {
1888                         case C_DOT:
1889                                 dotCount++;
1890                                 break;
1891                         case C_GENERIC_START:
1892                                 break indexFound;
1893                         case C_DOLLAR:
1894                                 break indexFound;
1895                 }
1896         }
1897         
1898         if(dotCount > 0) {
1899                 for(int i = 0; i < qualifiedType.length; i++) {
1900                         if(qualifiedType[i] == '.') {
1901                                 dotCount--;
1902                         }
1903                         if(dotCount <= 0) {
1904                                 return CharOperation.subarray(qualifiedType, i + 1, qualifiedType.length);
1905                         }
1906                 }
1907         }
1908         return qualifiedType;
1909 }
1910 /**
1911  * Returns type fragment of a type signature. The package fragment separator must be '.'
1912  * and the type fragment separator must be '$'.
1913  * <p>
1914  * For example:
1915  * <pre>
1916  * <code>
1917  * getSignatureSimpleName("Ljava.util.Map$Entry") -> "Map.Entry"
1918  * </code>
1919  * </pre>
1920  * </p>
1921  * 
1922  * @param typeSignature the type signature
1923  * @return the type fragment (separators are '.')
1924  * @since 3.1
1925  */
1926 public static String getSignatureSimpleName(String typeSignature) {
1927         return new String(getSignatureSimpleName(typeSignature == null ? null : typeSignature.toCharArray()));
1928 }
1929         
1930 /**
1931  * Returns the last segment of the given dot-separated qualified name.
1932  * Returns the given name if it is not qualified.
1933  * <p>
1934  * For example:
1935  * <pre>
1936  * <code>
1937  * getSimpleName({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {'O', 'b', 'j', 'e', 'c', 't'}
1938  * </code>
1939  * </pre>
1940  * </p>
1941  *
1942  * @param name the name
1943  * @return the last segment of the qualified name
1944  * @exception NullPointerException if name is null
1945  * @since 2.0
1946  */
1947 public static char[] getSimpleName(char[] name) {
1948
1949         int lastDot = -1, lastGenericStart = -1, lastGenericEnd = -1;
1950         int depth = 0;
1951         int length = name.length;
1952         lastDotLookup: for (int i = length -1; i >= 0; i--) {
1953                 switch (name[i]) {
1954                         case '.':
1955                                 if (depth == 0) {
1956                                         lastDot = i;
1957                                         break lastDotLookup;
1958                                 }
1959                                 break;
1960                         case '<':
1961                                 depth--;
1962                                 if (depth == 0) lastGenericStart = i;
1963                                 break;
1964                         case '>':
1965                                 if (depth == 0) lastGenericEnd = i;
1966                                 depth++;
1967                                 break;
1968                 }
1969         }
1970         if (lastGenericStart < 0) {
1971                 if (lastDot < 0) {
1972                         return name;
1973                 }
1974                 return  CharOperation.subarray(name, lastDot + 1, length);
1975         }
1976         StringBuffer buffer = new StringBuffer(10);
1977         int nameStart = lastDot < 0 ? 0 : lastDot+1;
1978         buffer.append(name, nameStart, lastGenericStart - nameStart);
1979         appendArgumentSimpleNames(name, lastGenericStart, lastGenericEnd, buffer);
1980         buffer.append(name, lastGenericEnd+1, length-lastGenericEnd-1); // copy trailing portion, may contain dimensions        
1981         char[] result = new char[length = buffer.length()];
1982         buffer.getChars(0, length, result, 0);
1983         return result;  
1984 }
1985 /**
1986  * Returns the last segment of the given dot-separated qualified name.
1987  * Returns the given name if it is not qualified.
1988  * <p>
1989  * For example:
1990  * <pre>
1991  * <code>
1992  * getSimpleName("java.lang.Object") -> "Object"
1993  * </code>
1994  * <code>
1995  * getSimpleName("java.util.Map<java.lang.String, java.lang.Object>") -> "Map<String,Object>"
1996  * </code>
1997  * </pre>
1998  * </p>
1999  *
2000  * @param name the name
2001  * @return the last segment of the qualified name
2002  * @exception NullPointerException if name is null
2003  */
2004 public static String getSimpleName(String name) {
2005         int lastDot = -1, lastGenericStart = -1, lastGenericEnd = -1;
2006         int depth = 0;
2007         int length = name.length();
2008         lastDotLookup: for (int i = length -1; i >= 0; i--) {
2009                 switch (name.charAt(i)) {
2010                         case '.':
2011                                 if (depth == 0) {
2012                                         lastDot = i;
2013                                         break lastDotLookup;
2014                                 }
2015                                 break;
2016                         case '<':
2017                                 depth--;
2018                                 if (depth == 0) lastGenericStart = i;
2019                                 break;
2020                         case '>':
2021                                 if (depth == 0) lastGenericEnd = i;
2022                                 depth++;
2023                                 break;
2024                 }
2025         }
2026         if (lastGenericStart < 0) {
2027                 if (lastDot < 0) {
2028                         return name;
2029                 }
2030                 return name.substring(lastDot + 1, length);
2031         }
2032         StringBuffer buffer = new StringBuffer(10);
2033         char[] nameChars = name.toCharArray();
2034         int nameStart = lastDot < 0 ? 0 : lastDot+1;
2035         buffer.append(nameChars, nameStart, lastGenericStart - nameStart);
2036         appendArgumentSimpleNames(nameChars, lastGenericStart, lastGenericEnd, buffer);
2037         buffer.append(nameChars, lastGenericEnd+1, length-lastGenericEnd-1); // copy trailing portion, may contain dimensions   
2038         return buffer.toString();
2039 }
2040
2041 private static void appendSimpleName(char[] name, int start, int end, StringBuffer buffer) {
2042         int lastDot = -1, lastGenericStart = -1, lastGenericEnd = -1;
2043         int depth = 0;
2044         if (name[start] == '?') { // wildcard
2045                 buffer.append("? "); //$NON-NLS-1$
2046                 int index = consumeWhitespace(name, start+1, end+1);
2047                 switch (name[index]) {
2048                         case 'e' :
2049                                 int checkPos = checkName(EXTENDS, name, index, end);
2050                             if (checkPos > 0) {
2051                                 buffer.append(EXTENDS).append(' ');
2052                                 index = consumeWhitespace(name, checkPos, end+1);
2053                                 }
2054                                 break;
2055                         case 's' :
2056                                 checkPos = checkName(SUPER, name, index, end+1);
2057                             if (checkPos > 0) {
2058                                 buffer.append(SUPER).append(' ');
2059                                 index = consumeWhitespace(name, checkPos, end+1);
2060                                 }
2061                                 break;
2062                 }
2063                 start = index; // leading segment got processed
2064         }
2065         lastDotLookup: for (int i = end; i >= start; i--) {
2066                 switch (name[i]) {
2067                         case '.':
2068                                 if (depth == 0) {
2069                                         lastDot = i;
2070                                         break lastDotLookup;
2071                                 }
2072                                 break;
2073                         case '<':
2074                                 depth--;
2075                                 if (depth == 0) lastGenericStart = i;
2076                                 break;
2077                         case '>':
2078                                 if (depth == 0) lastGenericEnd = i;
2079                                 depth++;
2080                                 break;
2081                 }
2082         }
2083         int nameStart = lastDot < 0 ? start : lastDot+1;
2084         int nameEnd = lastGenericStart < 0 ? end+1 : lastGenericStart;
2085         buffer.append(name, nameStart, nameEnd - nameStart);
2086         if (lastGenericStart >= 0) {
2087                 appendArgumentSimpleNames(name, lastGenericStart, lastGenericEnd, buffer);
2088                 buffer.append(name, lastGenericEnd+1, end - lastGenericEnd); // copy trailing portion, may contain dimensions
2089         }
2090 }
2091 // <x.y.z, a.b<c>.d<e.f>> --> <z,d<f>>
2092 private static void appendArgumentSimpleNames(char[] name, int start, int end, StringBuffer buffer) {
2093         buffer.append('<');
2094         int depth = 0;
2095         int argumentStart = -1;
2096         int argumentCount = 0;
2097         for (int i = start; i <= end; i++) {
2098                 switch(name[i]) {
2099                         case '<' :
2100                                 depth++;
2101                                 if (depth == 1) {
2102                                         argumentStart = i+1;
2103                                 }
2104                                 break;
2105                         case '>' : 
2106                                 if (depth == 1) {
2107                                         if (argumentCount > 0) buffer.append(',');
2108                                         appendSimpleName(name, argumentStart, i-1, buffer);
2109                                         argumentCount++;
2110                                 }
2111                                 depth--;
2112                                 break;
2113                         case ',' :
2114                                 if (depth == 1) {
2115                                         if (argumentCount > 0) buffer.append(',');
2116                                         appendSimpleName(name, argumentStart, i-1, buffer);
2117                                         argumentCount++;
2118                                         argumentStart = i+1;                                    
2119                                 }
2120                                 break;
2121                 }
2122         }
2123         buffer.append('>');
2124 }
2125 /**
2126  * Returns all segments of the given dot-separated qualified name.
2127  * Returns an array with only the given name if it is not qualified.
2128  * Returns an empty array if the name is empty.
2129  * <p>
2130  * For example:
2131  * <pre>
2132  * <code>
2133  * getSimpleNames({'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}) -> {{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'O', 'b', 'j', 'e', 'c', 't'}}
2134  * getSimpleNames({'O', 'b', 'j', 'e', 'c', 't'}) -> {{'O', 'b', 'j', 'e', 'c', 't'}}
2135  * getSimpleNames({}) -> {}
2136  * getSimpleNames({'j', 'a', 'v', 'a', '.', 'u', 't', 'i', 'l', '.', 'L', 'i', 's', 't', '<', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g', '>'}) -> {{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'L', 'i', 's', 't', '<', 'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'S', 't', 'r', 'i', 'n', 'g'}}
2137  * </code>
2138  * </pre>
2139  *
2140  * @param name the name
2141  * @return the list of simple names, possibly empty
2142  * @exception NullPointerException if name is null
2143  * @since 2.0
2144  */
2145 public static char[][] getSimpleNames(char[] name) {
2146         int length = name == null ? 0 : name.length;
2147         if (length == 0)
2148                 return CharOperation.NO_CHAR_CHAR;
2149         
2150         int wordCount = 1;
2151         countingWords: for (int i = 0; i < length; i++)
2152                 switch(name[i]) {
2153                         case C_DOT:
2154                                 wordCount++;
2155                                 break;
2156                         case C_GENERIC_START:
2157                                 break countingWords;
2158                 }
2159         char[][] split = new char[wordCount][];
2160         int last = 0, currentWord = 0;
2161         for (int i = 0; i < length; i++) {
2162                 if (name[i] == C_GENERIC_START) break;
2163                 if (name[i] == C_DOT) {
2164                         split[currentWord] = new char[i - last];
2165                         System.arraycopy(
2166                                 name,
2167                                 last,
2168                                 split[currentWord++],
2169                                 0,
2170                                 i - last);
2171                         last = i + 1;
2172                 }
2173         }
2174         split[currentWord] = new char[length - last];
2175         System.arraycopy(name, last, split[currentWord], 0, length - last);
2176         return split;
2177 }
2178 /**
2179  * Returns all segments of the given dot-separated qualified name.
2180  * Returns an array with only the given name if it is not qualified.
2181  * Returns an empty array if the name is empty.
2182  * <p>
2183  * For example:
2184  * <pre>
2185  * <code>
2186  * getSimpleNames("java.lang.Object") -> {"java", "lang", "Object"}
2187  * getSimpleNames("Object") -> {"Object"}
2188  * getSimpleNames("") -> {}
2189  * getSimpleNames("java.util.List<java.lang.String>") -> {"java", "lang", "List<java.lang.String"}
2190  * </code>
2191  * </pre>
2192  *
2193  * @param name the name
2194  * @return the list of simple names, possibly empty
2195  * @exception NullPointerException if name is null
2196  */
2197 public static String[] getSimpleNames(String name) {
2198         return CharOperation.toStrings(getSimpleNames(name.toCharArray()));
2199 }
2200 /**
2201  * Converts the given method signature to a readable form. The method signature is expected to
2202  * be dot-based.
2203  * <p>
2204  * For example:
2205  * <pre>
2206  * <code>
2207  * toString("([Ljava.lang.String;)V", "main", new String[] {"args"}, false, true) -> "void main(String[] args)"
2208  * </code>
2209  * </pre>
2210  * </p>
2211  * 
2212  * @param methodSignature the method signature to convert
2213  * @param methodName the name of the method to insert in the result, or 
2214  *   <code>null</code> if no method name is to be included
2215  * @param parameterNames the parameter names to insert in the result, or 
2216  *   <code>null</code> if no parameter names are to be included; if supplied,
2217  *   the number of parameter names must match that of the method signature
2218  * @param fullyQualifyTypeNames <code>true</code> if type names should be fully
2219  *   qualified, and <code>false</code> to use only simple names
2220  * @param includeReturnType <code>true</code> if the return type is to be
2221  *   included
2222  * @return the char array representation of the method signature
2223  * 
2224  * @since 2.0
2225  */
2226 public static char[] toCharArray(char[] methodSignature, char[] methodName, char[][] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType) {
2227         return toCharArray(methodSignature, methodName, parameterNames, fullyQualifyTypeNames, includeReturnType, false);
2228 }
2229 /**
2230  * Converts the given method signature to a readable form. The method signature is expected to
2231  * be dot-based.
2232  * <p>
2233  * For example:
2234  * <pre>
2235  * <code>
2236  * toString("([Ljava.lang.String;)V", "main", new String[] {"args"}, false, true) -> "void main(String[] args)"
2237  * </code>
2238  * </pre>
2239  * </p>
2240  * 
2241  * @param methodSignature the method signature to convert
2242  * @param methodName the name of the method to insert in the result, or 
2243  *   <code>null</code> if no method name is to be included
2244  * @param parameterNames the parameter names to insert in the result, or 
2245  *   <code>null</code> if no parameter names are to be included; if supplied,
2246  *   the number of parameter names must match that of the method signature
2247  * @param fullyQualifyTypeNames <code>true</code> if type names should be fully
2248  *   qualified, and <code>false</code> to use only simple names
2249  * @param includeReturnType <code>true</code> if the return type is to be
2250  *   included
2251  * @param isVargArgs <code>true</code> if the last argument should be displayed as a 
2252  * variable argument,  <code>false</code> otherwise.
2253  * @return the char array representation of the method signature
2254  * 
2255  * @since 3.1
2256  */
2257 public static char[] toCharArray(char[] methodSignature, char[] methodName, char[][] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType, boolean isVargArgs) {
2258         int firstParen = CharOperation.indexOf(C_PARAM_START, methodSignature);
2259         if (firstParen == -1) {
2260                 throw new IllegalArgumentException();
2261         }
2262         
2263         StringBuffer buffer = new StringBuffer(methodSignature.length + 10);
2264         
2265         // return type
2266         if (includeReturnType) {
2267                 char[] rts = getReturnType(methodSignature);
2268                 appendTypeSignature(rts, 0 , fullyQualifyTypeNames, buffer);
2269                 buffer.append(' ');
2270         }
2271         
2272         // selector
2273         if (methodName != null) {
2274                 buffer.append(methodName);
2275         }
2276         
2277         // parameters
2278         buffer.append('(');
2279         char[][] pts = getParameterTypes(methodSignature);
2280         for (int i = 0, max = pts.length; i < max; i++) {
2281                 if (i == max - 1) {
2282                         appendTypeSignature(pts[i], 0 , fullyQualifyTypeNames, buffer, isVargArgs);
2283                 } else {
2284                         appendTypeSignature(pts[i], 0 , fullyQualifyTypeNames, buffer);
2285                 }
2286                 if (parameterNames != null) {
2287                         buffer.append(' ');
2288                         buffer.append(parameterNames[i]);
2289                 }
2290                 if (i != pts.length - 1) {
2291                         buffer.append(',');
2292                         buffer.append(' ');
2293                 }
2294         }
2295         buffer.append(')');
2296         char[] result = new char[buffer.length()];
2297         buffer.getChars(0, buffer.length(), result, 0);
2298         return result;
2299 }
2300 /**
2301  * Converts the given type signature to a readable string. The signature is expected to
2302  * be dot-based.
2303  * 
2304  * <p>
2305  * For example:
2306  * <pre>
2307  * <code>
2308  * 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', '[', ']'}
2309  * toString({'I'}) -> {'i', 'n', 't'}
2310  * toString({'+', 'L', 'O', 'b', 'j', 'e', 'c', 't', ';'}) -> {'?', ' ', 'e', 'x', 't', 'e', 'n', 'd', 's', ' ', 'O', 'b', 'j', 'e', 'c', 't'}
2311  * </code>
2312  * </pre>
2313  * </p>
2314  * <p>
2315  * Note: This method assumes that a type signature containing a <code>'$'</code>
2316  * is an inner type signature. While this is correct in most cases, someone could 
2317  * define a non-inner type name containing a <code>'$'</code>. Handling this 
2318  * correctly in all cases would have required resolving the signature, which 
2319  * generally not feasible.
2320  * </p>
2321  *
2322  * @param signature the type signature
2323  * @return the string representation of the type
2324  * @exception IllegalArgumentException if the signature is not syntactically
2325  *   correct
2326  * 
2327  * @since 2.0
2328  */
2329 public static char[] toCharArray(char[] signature) throws IllegalArgumentException {
2330                 int sigLength = signature.length;
2331                 if (sigLength == 0 || signature[0] == C_PARAM_START || signature[0] == C_GENERIC_START) {
2332                         return toCharArray(signature, CharOperation.NO_CHAR, null, true, true);
2333                 }
2334                 
2335                 StringBuffer buffer = new StringBuffer(signature.length + 10);
2336                 appendTypeSignature(signature, 0, true, buffer);
2337                 char[] result = new char[buffer.length()];
2338                 buffer.getChars(0, buffer.length(), result, 0);
2339                 return result;
2340 }
2341
2342 /**
2343  * Scans the given string for a type signature starting at the given
2344  * index and appends it to the given buffer, and returns the index of the last
2345  * character.
2346  * 
2347  * @param string the signature string
2348  * @param start the 0-based character index of the first character
2349  * @param fullyQualifyTypeNames <code>true</code> if type names should be fully
2350  *   qualified, and <code>false</code> to use only simple names
2351  * @param buffer the string buffer to append to
2352  * @return the 0-based character index of the last character
2353  * @exception IllegalArgumentException if this is not a type signature
2354  * @see #scanTypeSignature(char[], int)
2355  */
2356 private static int appendTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
2357         return appendTypeSignature(string, start, fullyQualifyTypeNames, buffer, false);
2358 }
2359 /**
2360  * Scans the given string for a type signature starting at the given
2361  * index and appends it to the given buffer, and returns the index of the last
2362  * character.
2363  * 
2364  * @param string the signature string
2365  * @param start the 0-based character index of the first character
2366  * @param fullyQualifyTypeNames <code>true</code> if type names should be fully
2367  *   qualified, and <code>false</code> to use only simple names
2368  * @param buffer the string buffer to append to
2369  * @param isVarArgs <code>true</code> if the type must be displayed as a
2370  * variable argument, <code>false</code> otherwise. In this case, the type must be an array type
2371  * @return the 0-based character index of the last character
2372  * @exception IllegalArgumentException if this is not a type signature, or if isVarArgs is <code>true</code>,
2373  * and the type is not an array type signature.
2374  * @see #scanTypeSignature(char[], int)
2375  */
2376 private static int appendTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer, boolean isVarArgs) {
2377         // need a minimum 1 char
2378         if (start >= string.length) {
2379                 throw new IllegalArgumentException();
2380         }
2381         char c = string[start];
2382         if (isVarArgs) {
2383                 switch (c) {
2384                         case C_ARRAY :
2385                                 return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer, true);
2386                         case C_RESOLVED :
2387                         case C_UNRESOLVED :
2388                         case C_TYPE_VARIABLE :
2389                         case C_BOOLEAN :
2390                         case C_BYTE :
2391                         case C_CHAR :
2392                         case C_DOUBLE :
2393                         case C_FLOAT :
2394                         case C_INT :
2395                         case C_LONG :
2396                         case C_SHORT :
2397                         case C_VOID :
2398                         case C_STAR:
2399                         case C_EXTENDS:
2400                         case C_SUPER:
2401                         default:
2402                                 throw new IllegalArgumentException(); // a var args is an array type
2403                 }
2404         } else {
2405                 switch (c) {
2406                         case C_ARRAY :
2407                                 return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer);
2408                         case C_RESOLVED :
2409                         case C_UNRESOLVED :
2410                                 return appendClassTypeSignature(string, start, fullyQualifyTypeNames, buffer);
2411                         case C_TYPE_VARIABLE :
2412                                 int e = scanTypeVariableSignature(string, start);
2413                                 buffer.append(CharOperation.subarray(string, start + 1, e));
2414                                 return e;
2415                         case C_BOOLEAN :
2416                                 buffer.append(BOOLEAN);
2417                                 return start;
2418                         case C_BYTE :
2419                                 buffer.append(BYTE);
2420                                 return start;
2421                         case C_CHAR :
2422                                 buffer.append(CHAR);
2423                                 return start;
2424                         case C_DOUBLE :
2425                                 buffer.append(DOUBLE);
2426                                 return start;
2427                         case C_FLOAT :
2428                                 buffer.append(FLOAT);
2429                                 return start;
2430                         case C_INT :
2431                                 buffer.append(INT);
2432                                 return start;
2433                         case C_LONG :
2434                                 buffer.append(LONG);
2435                                 return start;
2436                         case C_SHORT :
2437                                 buffer.append(SHORT);
2438                                 return start;
2439                         case C_VOID :
2440                                 buffer.append(VOID);
2441                                 return start;
2442                         case C_STAR:
2443                         case C_EXTENDS:
2444                         case C_SUPER:
2445                                 return appendTypeArgumentSignature(string, start, fullyQualifyTypeNames, buffer);
2446                         default :
2447                                 throw new IllegalArgumentException();
2448                 }
2449         }
2450 }
2451 /**
2452  * Scans the given string for an array type signature starting at the given
2453  * index and appends it to the given buffer, and returns the index of the last
2454  * character.
2455  * 
2456  * @param string the signature string
2457  * @param start the 0-based character index of the first character
2458  * @param fullyQualifyTypeNames <code>true</code> if type names should be fully
2459  *   qualified, and <code>false</code> to use only simple names
2460  * @return the 0-based character index of the last character
2461  * @exception IllegalArgumentException if this is not an array type signature
2462  * @see #scanArrayTypeSignature(char[], int)
2463  */
2464 private static int appendArrayTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
2465         return appendArrayTypeSignature(string, start, fullyQualifyTypeNames, buffer, false);
2466 }
2467 /**
2468  * Scans the given string for an array type signature starting at the given
2469  * index and appends it to the given buffer, and returns the index of the last
2470  * character.
2471  * 
2472  * @param string the signature string
2473  * @param start the 0-based character index of the first character
2474  * @param fullyQualifyTypeNames <code>true</code> if type names should be fully
2475  *   qualified, and <code>false</code> to use only simple names
2476  * @param isVarArgs <code>true</code> if the array type must be displayed as a
2477  * variable argument, <code>false</code> otherwise
2478  * @return the 0-based character index of the last character
2479  * @exception IllegalArgumentException if this is not an array type signature
2480  * @see #scanArrayTypeSignature(char[], int)
2481  */
2482 private static int appendArrayTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer, boolean isVarArgs) {
2483         // need a minimum 2 char
2484         if (start >= string.length - 1) {
2485                 throw new IllegalArgumentException();
2486         }
2487         char c = string[start];
2488         if (c != C_ARRAY) { //$NON-NLS-1$
2489                 throw new IllegalArgumentException();
2490         }
2491         int e = appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer);
2492         if (isVarArgs) {
2493                 buffer.append('.').append('.').append('.');
2494         } else {
2495                 buffer.append('[').append(']');
2496         }
2497         return e;
2498 }
2499 /**
2500  * Scans the given string for a class type signature starting at the given
2501  * index and appends it to the given buffer, and returns the index of the last
2502  * character.
2503  * 
2504  * @param string the signature string
2505  * @param start the 0-based character index of the first character
2506  * @param fullyQualifyTypeNames <code>true</code> if type names should be fully
2507  *   qualified, and <code>false</code> to use only simple names
2508  * @param buffer the string buffer to append to
2509  * @return the 0-based character index of the last character
2510  * @exception IllegalArgumentException if this is not a class type signature
2511  * @see #scanClassTypeSignature(char[], int)
2512  */
2513 private static int appendClassTypeSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
2514         // need a minimum 3 chars "Lx;"
2515         if (start >= string.length - 2) { 
2516                 throw new IllegalArgumentException();
2517         }
2518         // must start in "L" or "Q"
2519         char c = string[start];
2520         if (c != C_RESOLVED && c != C_UNRESOLVED) {
2521                 throw new IllegalArgumentException();
2522         }
2523         boolean resolved = (c == C_RESOLVED);
2524         boolean removePackageQualifiers = !fullyQualifyTypeNames;
2525         if (!resolved) {
2526                 // keep everything in an unresolved name
2527                 removePackageQualifiers = false;
2528         }
2529         int p = start + 1;
2530         int checkpoint = buffer.length();
2531         while (true) {
2532                 if (p >= string.length) {
2533                         throw new IllegalArgumentException();
2534                 }
2535                 c = string[p];
2536                 switch(c) {
2537                         case C_SEMICOLON :
2538                                 // all done
2539                                 return p;
2540                         case C_GENERIC_START :
2541                                 int e = appendTypeArgumentSignatures(string, p, fullyQualifyTypeNames, buffer);
2542                                 // once we hit type arguments there are no more package prefixes
2543                                 removePackageQualifiers = false;
2544                                 p = e;
2545                                 break;
2546                         case C_DOT :
2547                                 if (removePackageQualifiers) {
2548                                         // erase package prefix
2549                                         buffer.setLength(checkpoint);
2550                                 } else {
2551                                         buffer.append('.');
2552                                 }
2553                                 break;
2554                          case '/' :
2555                                 if (removePackageQualifiers) {
2556                                         // erase package prefix
2557                                         buffer.setLength(checkpoint);
2558                                 } else {
2559                                         buffer.append('/');
2560                                 }
2561                                 break;
2562                          case C_DOLLAR :
2563                                 if (resolved) {
2564                                         // once we hit "$" there are no more package prefixes
2565                                         removePackageQualifiers = false;
2566                                         /**
2567                                          * Convert '$' in resolved type signatures into '.'.
2568                                          * NOTE: This assumes that the type signature is an inner type
2569                                          * signature. This is true in most cases, but someone can define a
2570                                          * non-inner type name containing a '$'.
2571                                          */
2572                                         buffer.append('.');
2573                                 }
2574                                 break;
2575                          default :
2576                                 buffer.append(c);
2577                 }
2578                 p++;
2579         }
2580 }
2581
2582 /**
2583  * Scans the given string for a list of type arguments signature starting at the
2584  * given index and appends it to the given buffer, and returns the index of the
2585  * last character.
2586  * 
2587  * @param string the signature string
2588  * @param start the 0-based character index of the first character
2589  * @param fullyQualifyTypeNames <code>true</code> if type names should be fully
2590  *   qualified, and <code>false</code> to use only simple names
2591  * @param buffer the string buffer to append to
2592  * @return the 0-based character index of the last character
2593  * @exception IllegalArgumentException if this is not a list of type argument
2594  * signatures
2595  * @see #scanTypeArgumentSignatures(char[], int)
2596  */
2597 private static int appendTypeArgumentSignatures(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
2598         // need a minimum 2 char "<>"
2599         if (start >= string.length - 1) {
2600                 throw new IllegalArgumentException();
2601         }
2602         char c = string[start];
2603         if (c != C_GENERIC_START) {
2604                 throw new IllegalArgumentException();
2605         }
2606         buffer.append('<');
2607         int p = start + 1;
2608         int count = 0;
2609         while (true) {
2610                 if (p >= string.length) {
2611                         throw new IllegalArgumentException();
2612                 }
2613                 c = string[p];
2614                 if (c == C_GENERIC_END) {
2615                         buffer.append('>');
2616                         return p;
2617                 }
2618                 if (count != 0) {
2619                         buffer.append(',');
2620                 }
2621                 int e = appendTypeArgumentSignature(string, p, fullyQualifyTypeNames, buffer);
2622                 count++;
2623                 p = e + 1;
2624         }
2625 }
2626
2627 /**
2628  * Scans the given string for a type argument signature starting at the given
2629  * index and appends it to the given buffer, and returns the index of the last
2630  * character.
2631  * 
2632  * @param string the signature string
2633  * @param start the 0-based character index of the first character
2634  * @param fullyQualifyTypeNames <code>true</code> if type names should be fully
2635  *   qualified, and <code>false</code> to use only simple names
2636  * @param buffer the string buffer to append to
2637  * @return the 0-based character index of the last character
2638  * @exception IllegalArgumentException if this is not a type argument signature
2639  * @see #scanTypeArgumentSignature(char[], int)
2640  */
2641 private static int appendTypeArgumentSignature(char[] string, int start, boolean fullyQualifyTypeNames, StringBuffer buffer) {
2642         // need a minimum 1 char
2643         if (start >= string.length) {
2644                 throw new IllegalArgumentException();
2645         }
2646         char c = string[start];
2647         switch(c) {
2648                 case C_STAR :
2649                         buffer.append('?');
2650                         return start;
2651                 case C_EXTENDS :
2652                         buffer.append("? extends "); //$NON-NLS-1$
2653                         return appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer);
2654                 case C_SUPER :
2655                         buffer.append("? super "); //$NON-NLS-1$
2656                         return appendTypeSignature(string, start + 1, fullyQualifyTypeNames, buffer);
2657                 default :
2658                         return appendTypeSignature(string, start, fullyQualifyTypeNames, buffer);
2659         }
2660 }
2661
2662 /**
2663  * Converts the given array of qualified name segments to a qualified name.
2664  * <p>
2665  * For example:
2666  * <pre>
2667  * <code>
2668  * toQualifiedName({{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'O', 'b', 'j', 'e', 'c', 't'}}) -> {'j', 'a', 'v', 'a', '.', 'l', 'a', 'n', 'g', '.', 'O', 'b', 'j', 'e', 'c', 't'}
2669  * toQualifiedName({{'O', 'b', 'j', 'e', 'c', 't'}}) -> {'O', 'b', 'j', 'e', 'c', 't'}
2670  * toQualifiedName({{}}) -> {}
2671  * </code>
2672  * </pre>
2673  * </p>
2674  *
2675  * @param segments the list of name segments, possibly empty
2676  * @return the dot-separated qualified name, or the empty string
2677  * 
2678  * @since 2.0
2679  */
2680 public static char[] toQualifiedName(char[][] segments) {
2681         int length = segments.length;
2682         if (length == 0) return CharOperation.NO_CHAR;
2683         if (length == 1) return segments[0];
2684         
2685         int resultLength = 0;
2686         for (int i = 0; i < length; i++) {
2687                 resultLength += segments[i].length+1;
2688         }
2689         resultLength--;
2690         char[] result = new char[resultLength];
2691         int index = 0;
2692         for (int i = 0; i < length; i++) {
2693                 char[] segment = segments[i];
2694                 int segmentLength = segment.length;
2695                 System.arraycopy(segment, 0, result, index, segmentLength);
2696                 index += segmentLength;
2697                 if (i != length-1) {
2698                         result[index++] = C_DOT;
2699                 }
2700         }
2701         return result;
2702 }
2703 /**
2704  * Converts the given array of qualified name segments to a qualified name.
2705  * <p>
2706  * For example:
2707  * <pre>
2708  * <code>
2709  * toQualifiedName(new String[] {"java", "lang", "Object"}) -> "java.lang.Object"
2710  * toQualifiedName(new String[] {"Object"}) -> "Object"
2711  * toQualifiedName(new String[0]) -> ""
2712  * </code>
2713  * </pre>
2714  * </p>
2715  *
2716  * @param segments the list of name segments, possibly empty
2717  * @return the dot-separated qualified name, or the empty string
2718  */
2719 public static String toQualifiedName(String[] segments) {
2720         int length = segments.length;
2721         char[][] charArrays = new char[length][];
2722         for (int i = 0; i < length; i++) {
2723                 charArrays[i] = segments[i].toCharArray();
2724         }
2725         return new String(toQualifiedName(charArrays));
2726 }
2727 /**
2728  * Converts the given type signature to a readable string. The signature is expected to
2729  * be dot-based.
2730  * 
2731  * <p>
2732  * For example:
2733  * <pre>
2734  * <code>
2735  * toString("[Ljava.lang.String;") -> "java.lang.String[]"
2736  * toString("I") -> "int"
2737  * toString("+QObject;") -> "? extends Object"
2738  * </code>
2739  * </pre>
2740  * </p>
2741  * <p>
2742  * Note: This method assumes that a type signature containing a <code>'$'</code>
2743  * is an inner type signature. While this is correct in most cases, someone could 
2744  * define a non-inner type name containing a <code>'$'</code>. Handling this 
2745  * correctly in all cases would have required resolving the signature, which 
2746  * generally not feasible.
2747  * </p>
2748  *
2749  * @param signature the type signature
2750  * @return the string representation of the type
2751  * @exception IllegalArgumentException if the signature is not syntactically
2752  *   correct
2753  */
2754 public static String toString(String signature) throws IllegalArgumentException {
2755         return new String(toCharArray(signature.toCharArray()));
2756 }
2757 /**
2758  * Converts the given method signature to a readable string. The method signature is expected to
2759  * be dot-based.
2760  * 
2761  * @param methodSignature the method signature to convert
2762  * @param methodName the name of the method to insert in the result, or 
2763  *   <code>null</code> if no method name is to be included
2764  * @param parameterNames the parameter names to insert in the result, or 
2765  *   <code>null</code> if no parameter names are to be included; if supplied,
2766  *   the number of parameter names must match that of the method signature
2767  * @param fullyQualifyTypeNames <code>true</code> if type names should be fully
2768  *   qualified, and <code>false</code> to use only simple names
2769  * @param includeReturnType <code>true</code> if the return type is to be
2770  *   included
2771  * @see #toCharArray(char[], char[], char[][], boolean, boolean)
2772  * @return the string representation of the method signature
2773  */
2774 public static String toString(String methodSignature, String methodName, String[] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType) {
2775         return toString(methodSignature, methodName, parameterNames, fullyQualifyTypeNames, includeReturnType, false);
2776 }
2777 /**
2778  * Converts the given method signature to a readable string. The method signature is expected to
2779  * be dot-based.
2780  * 
2781  * @param methodSignature the method signature to convert
2782  * @param methodName the name of the method to insert in the result, or 
2783  *   <code>null</code> if no method name is to be included
2784  * @param parameterNames the parameter names to insert in the result, or 
2785  *   <code>null</code> if no parameter names are to be included; if supplied,
2786  *   the number of parameter names must match that of the method signature
2787  * @param fullyQualifyTypeNames <code>true</code> if type names should be fully
2788  *   qualified, and <code>false</code> to use only simple names
2789  * @param includeReturnType <code>true</code> if the return type is to be
2790  *   included
2791  * @param isVarArgs <code>true</code> if the last argument should be displayed as a 
2792  * variable argument, <code>false</code> otherwise
2793  * @see #toCharArray(char[], char[], char[][], boolean, boolean)
2794  * @return the string representation of the method signature
2795  */
2796 public static String toString(String methodSignature, String methodName, String[] parameterNames, boolean fullyQualifyTypeNames, boolean includeReturnType, boolean isVarArgs) {
2797         char[][] params;
2798         if (parameterNames == null) {
2799                 params = null;
2800         } else {
2801                 int paramLength = parameterNames.length;
2802                 params = new char[paramLength][];
2803                 for (int i = 0; i < paramLength; i++) {
2804                         params[i] = parameterNames[i].toCharArray();
2805                 }
2806         }
2807         return new String(toCharArray(methodSignature.toCharArray(), methodName == null ? null : methodName.toCharArray(), params, fullyQualifyTypeNames, includeReturnType, isVarArgs));
2808 }
2809 }