1 /*******************************************************************************
2  * Copyright (c) 2000, 2013 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *     daolaf@gmail.com - Contribution for bug 3292227
11  *******************************************************************************/
12 package org.eclipse.jdt.internal.compiler.util;
13 
14 import java.io.BufferedInputStream;
15 import java.io.BufferedOutputStream;
16 import java.io.BufferedReader;
17 import java.io.ByteArrayInputStream;
18 import java.io.File;
19 import java.io.FileInputStream;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.PrintWriter;
25 import java.io.StringWriter;
26 import java.io.UnsupportedEncodingException;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.StringTokenizer;
30 import java.util.zip.ZipEntry;
31 import java.util.zip.ZipFile;
32 
33 import org.eclipse.jdt.core.compiler.CharOperation;
34 import org.eclipse.jdt.internal.compiler.ClassFile;
35 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
36 import org.eclipse.jdt.internal.compiler.batch.FileSystem;
37 import org.eclipse.jdt.internal.compiler.batch.Main;
38 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
39 import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
40 import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
41 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
42 import org.eclipse.jdt.internal.compiler.lookup.TagBits;
43 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
44 import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
45 import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
46 
47 @SuppressWarnings({"rawtypes", "unchecked"})
48 public class Util implements SuffixConstants {
49 
50 	/**
51 	 * Character constant indicating the primitive type boolean in a signature.
52 	 * Value is <code>'Z'</code>.
53 	 */
54 	public static final char C_BOOLEAN 		= 'Z';
55 
56 	/**
57 	 * Character constant indicating the primitive type byte in a signature.
58 	 * Value is <code>'B'</code>.
59 	 */
60 	public static final char C_BYTE 		= 'B';
61 
62 	/**
63 	 * Character constant indicating the primitive type char in a signature.
64 	 * Value is <code>'C'</code>.
65 	 */
66 	public static final char C_CHAR 		= 'C';
67 
68 	/**
69 	 * Character constant indicating the primitive type double in a signature.
70 	 * Value is <code>'D'</code>.
71 	 */
72 	public static final char C_DOUBLE 		= 'D';
73 
74 	/**
75 	 * Character constant indicating the primitive type float in a signature.
76 	 * Value is <code>'F'</code>.
77 	 */
78 	public static final char C_FLOAT 		= 'F';
79 
80 	/**
81 	 * Character constant indicating the primitive type int in a signature.
82 	 * Value is <code>'I'</code>.
83 	 */
84 	public static final char C_INT 			= 'I';
85 
86 	/**
87 	 * Character constant indicating the semicolon in a signature.
88 	 * Value is <code>';'</code>.
89 	 */
90 	public static final char C_SEMICOLON 			= ';';
91 
92 	/**
93 	 * Character constant indicating the colon in a signature.
94 	 * Value is <code>':'</code>.
95 	 * @since 3.0
96 	 */
97 	public static final char C_COLON 			= ':';
98 
99 	/**
100 	 * Character constant indicating the primitive type long in a signature.
101 	 * Value is <code>'J'</code>.
102 	 */
103 	public static final char C_LONG			= 'J';
104 
105 	/**
106 	 * Character constant indicating the primitive type short in a signature.
107 	 * Value is <code>'S'</code>.
108 	 */
109 	public static final char C_SHORT		= 'S';
110 
111 	/**
112 	 * Character constant indicating result type void in a signature.
113 	 * Value is <code>'V'</code>.
114 	 */
115 	public static final char C_VOID			= 'V';
116 
117 	/**
118 	 * Character constant indicating the start of a resolved type variable in a
119 	 * signature. Value is <code>'T'</code>.
120 	 * @since 3.0
121 	 */
122 	public static final char C_TYPE_VARIABLE	= 'T';
123 
124 	/**
125 	 * Character constant indicating an unbound wildcard type argument
126 	 * in a signature.
127 	 * Value is <code>'*'</code>.
128 	 * @since 3.0
129 	 */
130 	public static final char C_STAR	= '*';
131 
132 	/**
133 	 * Character constant indicating an exception in a signature.
134 	 * Value is <code>'^'</code>.
135 	 * @since 3.1
136 	 */
137 	public static final char C_EXCEPTION_START	= '^';
138 
139 	/**
140 	 * Character constant indicating a bound wildcard type argument
141 	 * in a signature with extends clause.
142 	 * Value is <code>'+'</code>.
143 	 * @since 3.1
144 	 */
145 	public static final char C_EXTENDS	= '+';
146 
147 	/**
148 	 * Character constant indicating a bound wildcard type argument
149 	 * in a signature with super clause.
150 	 * Value is <code>'-'</code>.
151 	 * @since 3.1
152 	 */
153 	public static final char C_SUPER	= '-';
154 
155 	/**
156 	 * Character constant indicating the dot in a signature.
157 	 * Value is <code>'.'</code>.
158 	 */
159 	public static final char C_DOT			= '.';
160 
161 	/**
162 	 * Character constant indicating the dollar in a signature.
163 	 * Value is <code>'$'</code>.
164 	 */
165 	public static final char C_DOLLAR			= '$';
166 
167 	/**
168 	 * Character constant indicating an array type in a signature.
169 	 * Value is <code>'['</code>.
170 	 */
171 	public static final char C_ARRAY		= '[';
172 
173 	/**
174 	 * Character constant indicating the start of a resolved, named type in a
175 	 * signature. Value is <code>'L'</code>.
176 	 */
177 	public static final char C_RESOLVED		= 'L';
178 
179 	/**
180 	 * Character constant indicating the start of an unresolved, named type in a
181 	 * signature. Value is <code>'Q'</code>.
182 	 */
183 	public static final char C_UNRESOLVED	= 'Q';
184 
185 	/**
186 	 * Character constant indicating the end of a named type in a signature.
187 	 * Value is <code>';'</code>.
188 	 */
189 	public static final char C_NAME_END		= ';';
190 
191 	/**
192 	 * Character constant indicating the start of a parameter type list in a
193 	 * signature. Value is <code>'('</code>.
194 	 */
195 	public static final char C_PARAM_START	= '(';
196 
197 	/**
198 	 * Character constant indicating the end of a parameter type list in a
199 	 * signature. Value is <code>')'</code>.
200 	 */
201 	public static final char C_PARAM_END	= ')';
202 
203 	/**
204 	 * Character constant indicating the start of a formal type parameter
205 	 * (or type argument) list in a signature. Value is <code>'&lt;'</code>.
206 	 * @since 3.0
207 	 */
208 	public static final char C_GENERIC_START	= '<';
209 
210 	/**
211 	 * Character constant indicating the end of a generic type list in a
212 	 * signature. Value is <code>'&gt;'</code>.
213 	 * @since 3.0
214 	 */
215 	public static final char C_GENERIC_END	= '>';
216 
217 	/**
218 	 * Character constant indicating a capture of a wildcard type in a
219 	 * signature. Value is <code>'!'</code>.
220 	 * @since 3.1
221 	 */
222 	public static final char C_CAPTURE	= '!';
223 
224 	public interface Displayable {
displayString(Object o)225 		String displayString(Object o);
226 	}
227 
228 	private static final int DEFAULT_READING_SIZE = 8192;
229 	private static final int DEFAULT_WRITING_SIZE = 1024;
230 	public final static String UTF_8 = "UTF-8";	//$NON-NLS-1$
231 	public static final String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$
232 
233 	public static final String EMPTY_STRING = new String(CharOperation.NO_CHAR);
234 	public static final int[] EMPTY_INT_ARRAY= new int[0];
235 
236 	/**
237 	 * Build all the directories and subdirectories corresponding to the packages names
238 	 * into the directory specified in parameters.
239 	 *
240 	 * outputPath is formed like:
241 	 *	   c:\temp\ the last character is a file separator
242 	 * relativeFileName is formed like:
243 	 *     java\lang\String.class *
244 	 *
245 	 * @param outputPath java.lang.String
246 	 * @param relativeFileName java.lang.String
247 	 * @return java.lang.String
248 	 */
buildAllDirectoriesInto(String outputPath, String relativeFileName)249 	public static String buildAllDirectoriesInto(String outputPath, String relativeFileName) throws IOException {
250 		char fileSeparatorChar = File.separatorChar;
251 		String fileSeparator = File.separator;
252 		File f;
253 		outputPath = outputPath.replace('/', fileSeparatorChar);
254 			// these could be optimized out if we normalized paths once and for
255 			// all
256 		relativeFileName = relativeFileName.replace('/', fileSeparatorChar);
257 		String outputDirPath, fileName;
258 		int separatorIndex = relativeFileName.lastIndexOf(fileSeparatorChar);
259 		if (separatorIndex == -1) {
260 			if (outputPath.endsWith(fileSeparator)) {
261 				outputDirPath = outputPath.substring(0, outputPath.length() - 1);
262 				fileName = outputPath + relativeFileName;
263 			} else {
264 				outputDirPath = outputPath;
265 				fileName = outputPath + fileSeparator + relativeFileName;
266 			}
267 		} else {
268 			if (outputPath.endsWith(fileSeparator)) {
269 				outputDirPath = outputPath +
270 					relativeFileName.substring(0, separatorIndex);
271 				fileName = outputPath + relativeFileName;
272 			} else {
273 				outputDirPath = outputPath + fileSeparator +
274 					relativeFileName.substring(0, separatorIndex);
275 				fileName = outputPath + fileSeparator + relativeFileName;
276 			}
277 		}
278 		f = new File(outputDirPath);
279 		f.mkdirs();
280 		if (f.isDirectory()) {
281 			return fileName;
282 		} else {
283 			// the directory creation failed for some reason - retry using
284 			// a slower algorithm so as to refine the diagnostic
285 			if (outputPath.endsWith(fileSeparator)) {
286 				outputPath = outputPath.substring(0, outputPath.length() - 1);
287 			}
288 			f = new File(outputPath);
289 			boolean checkFileType = false;
290 			if (f.exists()) {
291 				  checkFileType = true; // pre-existed
292 			} else {
293 				// we have to create that directory
294 				if (!f.mkdirs()) {
295 					  if (f.exists()) {
296 							// someone else created f -- need to check its type
297 							checkFileType = true;
298 					  } else {
299 							// no one could create f -- complain
300 						throw new IOException(Messages.bind(
301 							Messages.output_notValidAll, f.getAbsolutePath()));
302 					  }
303 				}
304 			}
305 			if (checkFileType) {
306 				  if (!f.isDirectory()) {
307 					throw new IOException(Messages.bind(
308 						Messages.output_isFile, f.getAbsolutePath()));
309 				  }
310 			}
311 			StringBuffer outDir = new StringBuffer(outputPath);
312 			outDir.append(fileSeparator);
313 			StringTokenizer tokenizer =
314 				new StringTokenizer(relativeFileName, fileSeparator);
315 			String token = tokenizer.nextToken();
316 			while (tokenizer.hasMoreTokens()) {
317 				f = new File(outDir.append(token).append(fileSeparator).toString());
318 				  checkFileType = false; // reset
319 				if (f.exists()) {
320 					  checkFileType = true; // this is suboptimal, but it catches corner cases
321 											// in which a regular file pre-exists
322 				} else {
323 				// we have to create that directory
324 					if (!f.mkdir()) {
325 						  if (f.exists()) {
326 								// someone else created f -- need to check its type
327 								checkFileType = true;
328 						  } else {
329 								// no one could create f -- complain
330 							throw new IOException(Messages.bind(
331 								Messages.output_notValid,
332 									outDir.substring(outputPath.length() + 1,
333 										outDir.length() - 1),
334 									outputPath));
335 						  }
336 					}
337 				}
338 				if (checkFileType) {
339 					  if (!f.isDirectory()) {
340 						throw new IOException(Messages.bind(
341 							Messages.output_isFile, f.getAbsolutePath()));
342 					  }
343 				}
344 				token = tokenizer.nextToken();
345 			}
346 			// token contains the last one
347 			return outDir.append(token).toString();
348 		}
349 	}
350 
351 	/**
352 	 * Returns the given bytes as a char array using a given encoding (null means platform default).
353 	 */
bytesToChar(byte[] bytes, String encoding)354 	public static char[] bytesToChar(byte[] bytes, String encoding) throws IOException {
355 
356 		return getInputStreamAsCharArray(new ByteArrayInputStream(bytes), bytes.length, encoding);
357 
358 	}
359 
360 	/**
361 	 * Returns the outer most enclosing type's visibility for the given TypeDeclaration
362 	 * and visibility based on compiler options.
363 	 */
computeOuterMostVisibility(TypeDeclaration typeDeclaration, int visibility)364 	public static int computeOuterMostVisibility(TypeDeclaration typeDeclaration, int visibility) {
365 		while (typeDeclaration != null) {
366 			switch (typeDeclaration.modifiers & ExtraCompilerModifiers.AccVisibilityMASK) {
367 				case ClassFileConstants.AccPrivate:
368 					visibility = ClassFileConstants.AccPrivate;
369 					break;
370 				case ClassFileConstants.AccDefault:
371 					if (visibility != ClassFileConstants.AccPrivate) {
372 						visibility = ClassFileConstants.AccDefault;
373 					}
374 					break;
375 				case ClassFileConstants.AccProtected:
376 					if (visibility == ClassFileConstants.AccPublic) {
377 						visibility = ClassFileConstants.AccProtected;
378 					}
379 					break;
380 			}
381 			typeDeclaration = typeDeclaration.enclosingType;
382 		}
383 		return visibility;
384 	}
385 	/**
386 	 * Returns the contents of the given file as a byte array.
387 	 * @throws IOException if a problem occured reading the file.
388 	 */
getFileByteContent(File file)389 	public static byte[] getFileByteContent(File file) throws IOException {
390 		InputStream stream = null;
391 		try {
392 			stream = new BufferedInputStream(new FileInputStream(file));
393 			return getInputStreamAsByteArray(stream, (int) file.length());
394 		} finally {
395 			if (stream != null) {
396 				try {
397 					stream.close();
398 				} catch (IOException e) {
399 					// ignore
400 				}
401 			}
402 		}
403 	}
404 	/**
405 	 * Returns the contents of the given file as a char array.
406 	 * When encoding is null, then the platform default one is used
407 	 * @throws IOException if a problem occured reading the file.
408 	 */
getFileCharContent(File file, String encoding)409 	public static char[] getFileCharContent(File file, String encoding) throws IOException {
410 		InputStream stream = null;
411 		try {
412 			stream = new FileInputStream(file);
413 			return getInputStreamAsCharArray(stream, (int) file.length(), encoding);
414 		} finally {
415 			if (stream != null) {
416 				try {
417 					stream.close();
418 				} catch (IOException e) {
419 					// ignore
420 				}
421 			}
422 		}
423 	}
getFileOutputStream(boolean generatePackagesStructure, String outputPath, String relativeFileName)424 	private static FileOutputStream getFileOutputStream(boolean generatePackagesStructure, String outputPath, String relativeFileName) throws IOException {
425 		if (generatePackagesStructure) {
426 			return new FileOutputStream(new File(buildAllDirectoriesInto(outputPath, relativeFileName)));
427 		} else {
428 			String fileName = null;
429 			char fileSeparatorChar = File.separatorChar;
430 			String fileSeparator = File.separator;
431 			// First we ensure that the outputPath exists
432 			outputPath = outputPath.replace('/', fileSeparatorChar);
433 			// To be able to pass the mkdirs() method we need to remove the extra file separator at the end of the outDir name
434 			int indexOfPackageSeparator = relativeFileName.lastIndexOf(fileSeparatorChar);
435 			if (indexOfPackageSeparator == -1) {
436 				if (outputPath.endsWith(fileSeparator)) {
437 					fileName = outputPath + relativeFileName;
438 				} else {
439 					fileName = outputPath + fileSeparator + relativeFileName;
440 				}
441 			} else {
442 				int length = relativeFileName.length();
443 				if (outputPath.endsWith(fileSeparator)) {
444 					fileName = outputPath + relativeFileName.substring(indexOfPackageSeparator + 1, length);
445 				} else {
446 					fileName = outputPath + fileSeparator + relativeFileName.substring(indexOfPackageSeparator + 1, length);
447 				}
448 			}
449 			return new FileOutputStream(new File(fileName));
450 		}
451 	}
452 
453 	/*
454 	 * NIO support to get input stream as byte array.
455 	 * Not used as with JDK 1.4.2 this support is slower than standard IO one...
456 	 * Keep it as comment for future in case of next JDK versions improve performance
457 	 * in this area...
458 	 *
459 	public static byte[] getInputStreamAsByteArray(FileInputStream stream, int length)
460 		throws IOException {
461 
462 		FileChannel channel = stream.getChannel();
463 		int size = (int)channel.size();
464 		if (length >= 0 && length < size) size = length;
465 		byte[] contents = new byte[size];
466 		ByteBuffer buffer = ByteBuffer.wrap(contents);
467 		channel.read(buffer);
468 		return contents;
469 	}
470 	*/
471 	/**
472 	 * Returns the given input stream's contents as a byte array.
473 	 * If a length is specified (i.e. if length != -1), only length bytes
474 	 * are returned. Otherwise all bytes in the stream are returned.
475 	 * Note this doesn't close the stream.
476 	 * @throws IOException if a problem occured reading the stream.
477 	 */
getInputStreamAsByteArray(InputStream stream, int length)478 	public static byte[] getInputStreamAsByteArray(InputStream stream, int length)
479 			throws IOException {
480 		byte[] contents;
481 		if (length == -1) {
482 			contents = new byte[0];
483 			int contentsLength = 0;
484 			int amountRead = -1;
485 			do {
486 				int amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE);  // read at least 8K
487 
488 				// resize contents if needed
489 				if (contentsLength + amountRequested > contents.length) {
490 					System.arraycopy(
491 						contents,
492 						0,
493 						contents = new byte[contentsLength + amountRequested],
494 						0,
495 						contentsLength);
496 				}
497 
498 				// read as many bytes as possible
499 				amountRead = stream.read(contents, contentsLength, amountRequested);
500 
501 				if (amountRead > 0) {
502 					// remember length of contents
503 					contentsLength += amountRead;
504 				}
505 			} while (amountRead != -1);
506 
507 			// resize contents if necessary
508 			if (contentsLength < contents.length) {
509 				System.arraycopy(
510 					contents,
511 					0,
512 					contents = new byte[contentsLength],
513 					0,
514 					contentsLength);
515 			}
516 		} else {
517 			contents = new byte[length];
518 			int len = 0;
519 			int readSize = 0;
520 			while ((readSize != -1) && (len != length)) {
521 				// See PR 1FMS89U
522 				// We record first the read size. In this case len is the actual read size.
523 				len += readSize;
524 				readSize = stream.read(contents, len, length - len);
525 			}
526 		}
527 
528 		return contents;
529 	}
530 
531 	/*
532 	 * NIO support to get input stream as char array.
533 	 * Not used as with JDK 1.4.2 this support is slower than standard IO one...
534 	 * Keep it as comment for future in case of next JDK versions improve performance
535 	 * in this area...
536 	public static char[] getInputStreamAsCharArray(FileInputStream stream, int length, String encoding)
537 		throws IOException {
538 
539 		FileChannel channel = stream.getChannel();
540 		int size = (int)channel.size();
541 		if (length >= 0 && length < size) size = length;
542 		Charset charset = encoding==null?systemCharset:Charset.forName(encoding);
543 		if (charset != null) {
544 			MappedByteBuffer bbuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
545 		    CharsetDecoder decoder = charset.newDecoder();
546 		    CharBuffer buffer = decoder.decode(bbuffer);
547 		    char[] contents = new char[buffer.limit()];
548 		    buffer.get(contents);
549 		    return contents;
550 		}
551 		throw new UnsupportedCharsetException(SYSTEM_FILE_ENCODING);
552 	}
553 	*/
554 	/**
555 	 * Returns the given input stream's contents as a character array.
556 	 * If a length is specified (i.e. if length != -1), this represents the number of bytes in the stream.
557 	 * Note this doesn't close the stream.
558 	 * @throws IOException if a problem occured reading the stream.
559 	 */
getInputStreamAsCharArray(InputStream stream, int length, String encoding)560 	public static char[] getInputStreamAsCharArray(InputStream stream, int length, String encoding)
561 			throws IOException {
562 		BufferedReader reader = null;
563 		try {
564 			reader = encoding == null
565 						? new BufferedReader(new InputStreamReader(stream))
566 						: new BufferedReader(new InputStreamReader(stream, encoding));
567 		} catch (UnsupportedEncodingException e) {
568 			// encoding is not supported
569 			reader =  new BufferedReader(new InputStreamReader(stream));
570 		}
571 		char[] contents;
572 		int totalRead = 0;
573 		if (length == -1) {
574 			contents = CharOperation.NO_CHAR;
575 		} else {
576 			// length is a good guess when the encoding produces less or the same amount of characters than the file length
577 			contents = new char[length]; // best guess
578 		}
579 
580 		while (true) {
581 			int amountRequested;
582 			if (totalRead < length) {
583 				// until known length is met, reuse same array sized eagerly
584 				amountRequested = length - totalRead;
585 			} else {
586 				// reading beyond known length
587 				int current = reader.read();
588 				if (current < 0) break;
589 
590 				amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE);  // read at least 8K
591 
592 				// resize contents if needed
593 				if (totalRead + 1 + amountRequested > contents.length)
594 					System.arraycopy(contents, 	0, 	contents = new char[totalRead + 1 + amountRequested], 0, totalRead);
595 
596 				// add current character
597 				contents[totalRead++] = (char) current; // coming from totalRead==length
598 			}
599 			// read as many chars as possible
600 			int amountRead = reader.read(contents, totalRead, amountRequested);
601 			if (amountRead < 0) break;
602 			totalRead += amountRead;
603 		}
604 
605 		// Do not keep first character for UTF-8 BOM encoding
606 		int start = 0;
607 		if (totalRead > 0 && UTF_8.equals(encoding)) {
608 			if (contents[0] == 0xFEFF) { // if BOM char then skip
609 				totalRead--;
610 				start = 1;
611 			}
612 		}
613 
614 		// resize contents if necessary
615 		if (totalRead < contents.length)
616 			System.arraycopy(contents, start, contents = new char[totalRead], 	0, 	totalRead);
617 
618 		return contents;
619 	}
620 
621 	/**
622 	 * Returns a one line summary for an exception (extracted from its stacktrace: name + first frame)
623 	 * @param exception
624 	 * @return one line summary for an exception
625 	 */
getExceptionSummary(Throwable exception)626 	public static String getExceptionSummary(Throwable exception) {
627 		StringWriter stringWriter = new StringWriter();
628 		exception.printStackTrace(new PrintWriter(stringWriter));
629 		StringBuffer buffer = stringWriter.getBuffer();
630 		StringBuffer exceptionBuffer = new StringBuffer(50);
631 		exceptionBuffer.append(exception.toString());
632 		// only keep leading frame portion of the trace (i.e. line no. 2 from the stacktrace)
633 		lookupLine2: for (int i = 0, lineSep = 0, max = buffer.length(), line2Start = 0; i < max; i++) {
634 			switch (buffer.charAt(i)) {
635 				case '\n':
636 				case '\r' :
637 					if (line2Start > 0) {
638 						exceptionBuffer.append(' ').append(buffer.substring(line2Start, i));
639 						break lookupLine2;
640 					}
641 					lineSep++;
642 					break;
643 				case ' ' :
644 				case '\t' :
645 					break;
646 				default :
647 					if (lineSep > 0) {
648 						line2Start = i;
649 						lineSep = 0;
650 					}
651 					break;
652 			}
653 		}
654 		return exceptionBuffer.toString();
655 	}
656 
getLineNumber(int position, int[] lineEnds, int g, int d)657 	public static int getLineNumber(int position, int[] lineEnds, int g, int d) {
658 		if (lineEnds == null)
659 			return 1;
660 		if (d == -1)
661 			return 1;
662 		int m = g, start;
663 		while (g <= d) {
664 			m = g + (d - g) /2;
665 			if (position < (start = lineEnds[m])) {
666 				d = m-1;
667 			} else if (position > start) {
668 				g = m+1;
669 			} else {
670 				return m + 1;
671 			}
672 		}
673 		if (position < lineEnds[m]) {
674 			return m+1;
675 		}
676 		return m+2;
677 	}
678 	/**
679 	 * Returns the contents of the given zip entry as a byte array.
680 	 * @throws IOException if a problem occured reading the zip entry.
681 	 */
getZipEntryByteContent(ZipEntry ze, ZipFile zip)682 	public static byte[] getZipEntryByteContent(ZipEntry ze, ZipFile zip)
683 		throws IOException {
684 
685 		InputStream stream = null;
686 		try {
687 			InputStream inputStream = zip.getInputStream(ze);
688 			if (inputStream == null) throw new IOException("Invalid zip entry name : " + ze.getName()); //$NON-NLS-1$
689 			stream = new BufferedInputStream(inputStream);
690 			return getInputStreamAsByteArray(stream, (int) ze.getSize());
691 		} finally {
692 			if (stream != null) {
693 				try {
694 					stream.close();
695 				} catch (IOException e) {
696 					// ignore
697 				}
698 			}
699 		}
700 	}
hashCode(Object[] array)701 	public static int hashCode(Object[] array) {
702 		int prime = 31;
703 		if (array == null) {
704 			return 0;
705 		}
706 		int result = 1;
707 		for (int index = 0; index < array.length; index++) {
708 			result = prime * result + (array[index] == null ? 0 : array[index].hashCode());
709 		}
710 		return result;
711 	}
712 	/**
713 	 * Returns whether the given name is potentially a zip archive file name
714 	 * (it has a file extension and it is not ".java" nor ".class")
715 	 */
isPotentialZipArchive(String name)716 	public final static boolean isPotentialZipArchive(String name) {
717 		int lastDot = name.lastIndexOf('.');
718 		if (lastDot == -1)
719 			return false; // no file extension, it cannot be a zip archive name
720 		if (name.lastIndexOf(File.separatorChar) > lastDot)
721 			return false; // dot was before the last file separator, it cannot be a zip archive name
722 		int length = name.length();
723 		int extensionLength = length - lastDot - 1;
724 		if (extensionLength == EXTENSION_java.length()) {
725 			for (int i = extensionLength-1; i >=0; i--) {
726 				if (Character.toLowerCase(name.charAt(length - extensionLength + i)) != EXTENSION_java.charAt(i)) {
727 					break; // not a ".java" file, check ".class" file case below
728 				}
729 				if (i == 0) {
730 					return false; // it is a ".java" file, it cannot be a zip archive name
731 				}
732 			}
733 		}
734 		if (extensionLength == EXTENSION_class.length()) {
735 			for (int i = extensionLength-1; i >=0; i--) {
736 				if (Character.toLowerCase(name.charAt(length - extensionLength + i)) != EXTENSION_class.charAt(i)) {
737 					return true; // not a ".class" file, so this is a potential archive name
738 				}
739 			}
740 			return false; // it is a ".class" file, it cannot be a zip archive name
741 		}
742 		return true; // it is neither a ".java" file nor a ".class" file, so this is a potential archive name
743 	}
744 
745 	/**
746 	 * Returns true iff str.toLowerCase().endsWith(".class")
747 	 * implementation is not creating extra strings.
748 	 */
isClassFileName(char[] name)749 	public final static boolean isClassFileName(char[] name) {
750 		int nameLength = name == null ? 0 : name.length;
751 		int suffixLength = SUFFIX_CLASS.length;
752 		if (nameLength < suffixLength) return false;
753 
754 		for (int i = 0, offset = nameLength - suffixLength; i < suffixLength; i++) {
755 			char c = name[offset + i];
756 			if (c != SUFFIX_class[i] && c != SUFFIX_CLASS[i]) return false;
757 		}
758 		return true;
759 	}
760 	/**
761 	 * Returns true iff str.toLowerCase().endsWith(".class")
762 	 * implementation is not creating extra strings.
763 	 */
isClassFileName(String name)764 	public final static boolean isClassFileName(String name) {
765 		int nameLength = name == null ? 0 : name.length();
766 		int suffixLength = SUFFIX_CLASS.length;
767 		if (nameLength < suffixLength) return false;
768 
769 		for (int i = 0; i < suffixLength; i++) {
770 			char c = name.charAt(nameLength - i - 1);
771 			int suffixIndex = suffixLength - i - 1;
772 			if (c != SUFFIX_class[suffixIndex] && c != SUFFIX_CLASS[suffixIndex]) return false;
773 		}
774 		return true;
775 	}
776 	/* TODO (philippe) should consider promoting it to CharOperation
777 	 * Returns whether the given resource path matches one of the inclusion/exclusion
778 	 * patterns.
779 	 * NOTE: should not be asked directly using pkg root pathes
780 	 * @see IClasspathEntry#getInclusionPatterns
781 	 * @see IClasspathEntry#getExclusionPatterns
782 	 */
isExcluded(char[] path, char[][] inclusionPatterns, char[][] exclusionPatterns, boolean isFolderPath)783 	public final static boolean isExcluded(char[] path, char[][] inclusionPatterns, char[][] exclusionPatterns, boolean isFolderPath) {
784 		if (inclusionPatterns == null && exclusionPatterns == null) return false;
785 
786 		inclusionCheck: if (inclusionPatterns != null) {
787 			for (int i = 0, length = inclusionPatterns.length; i < length; i++) {
788 				char[] pattern = inclusionPatterns[i];
789 				char[] folderPattern = pattern;
790 				if (isFolderPath) {
791 					int lastSlash = CharOperation.lastIndexOf('/', pattern);
792 					if (lastSlash != -1 && lastSlash != pattern.length-1){ // trailing slash -> adds '**' for free (see http://ant.apache.org/manual/dirtasks.html)
793 						int star = CharOperation.indexOf('*', pattern, lastSlash);
794 						if ((star == -1
795 								|| star >= pattern.length-1
796 								|| pattern[star+1] != '*')) {
797 							folderPattern = CharOperation.subarray(pattern, 0, lastSlash);
798 						}
799 					}
800 				}
801 				if (CharOperation.pathMatch(folderPattern, path, true, '/')) {
802 					break inclusionCheck;
803 				}
804 			}
805 			return true; // never included
806 		}
807 		if (isFolderPath) {
808 			path = CharOperation.concat(path, new char[] {'*'}, '/');
809 		}
810 		if (exclusionPatterns != null) {
811 			for (int i = 0, length = exclusionPatterns.length; i < length; i++) {
812 				if (CharOperation.pathMatch(exclusionPatterns[i], path, true, '/')) {
813 					return true;
814 				}
815 			}
816 		}
817 		return false;
818 	}
819 
820 	/**
821 	 * Returns true iff str.toLowerCase().endsWith(".java")
822 	 * implementation is not creating extra strings.
823 	 */
isJavaFileName(char[] name)824 	public final static boolean isJavaFileName(char[] name) {
825 		int nameLength = name == null ? 0 : name.length;
826 		int suffixLength = SUFFIX_JAVA.length;
827 		if (nameLength < suffixLength) return false;
828 
829 		for (int i = 0, offset = nameLength - suffixLength; i < suffixLength; i++) {
830 			char c = name[offset + i];
831 			if (c != SUFFIX_java[i] && c != SUFFIX_JAVA[i]) return false;
832 		}
833 		return true;
834 	}
835 
836 	/**
837 	 * Returns true iff str.toLowerCase().endsWith(".java")
838 	 * implementation is not creating extra strings.
839 	 */
isJavaFileName(String name)840 	public final static boolean isJavaFileName(String name) {
841 		int nameLength = name == null ? 0 : name.length();
842 		int suffixLength = SUFFIX_JAVA.length;
843 		if (nameLength < suffixLength) return false;
844 
845 		for (int i = 0; i < suffixLength; i++) {
846 			char c = name.charAt(nameLength - i - 1);
847 			int suffixIndex = suffixLength - i - 1;
848 			if (c != SUFFIX_java[suffixIndex] && c != SUFFIX_JAVA[suffixIndex]) return false;
849 		}
850 		return true;
851 	}
852 
reverseQuickSort(char[][] list, int left, int right)853 	public static void reverseQuickSort(char[][] list, int left, int right) {
854 		int original_left= left;
855 		int original_right= right;
856 		char[] mid= list[left + ((right-left)/2)];
857 		do {
858 			while (CharOperation.compareTo(list[left], mid) > 0) {
859 				left++;
860 			}
861 			while (CharOperation.compareTo(mid, list[right]) > 0) {
862 				right--;
863 			}
864 			if (left <= right) {
865 				char[] tmp= list[left];
866 				list[left]= list[right];
867 				list[right]= tmp;
868 				left++;
869 				right--;
870 			}
871 		} while (left <= right);
872 		if (original_left < right) {
873 			reverseQuickSort(list, original_left, right);
874 		}
875 		if (left < original_right) {
876 			reverseQuickSort(list, left, original_right);
877 		}
878 	}
reverseQuickSort(char[][] list, int left, int right, int[] result)879 	public static void reverseQuickSort(char[][] list, int left, int right, int[] result) {
880 		int original_left= left;
881 		int original_right= right;
882 		char[] mid= list[left + ((right-left)/2)];
883 		do {
884 			while (CharOperation.compareTo(list[left], mid) > 0) {
885 				left++;
886 			}
887 			while (CharOperation.compareTo(mid, list[right]) > 0) {
888 				right--;
889 			}
890 			if (left <= right) {
891 				char[] tmp= list[left];
892 				list[left]= list[right];
893 				list[right]= tmp;
894 				int temp = result[left];
895 				result[left] = result[right];
896 				result[right] = temp;
897 				left++;
898 				right--;
899 			}
900 		} while (left <= right);
901 		if (original_left < right) {
902 			reverseQuickSort(list, original_left, right, result);
903 		}
904 		if (left < original_right) {
905 			reverseQuickSort(list, left, original_right, result);
906 		}
907 	}
908 	/**
909 	 * INTERNAL USE-ONLY
910 	 * Search the column number corresponding to a specific position
911 	 */
searchColumnNumber(int[] startLineIndexes, int lineNumber, int position)912 	public static final int searchColumnNumber(int[] startLineIndexes, int lineNumber, int position) {
913 		switch(lineNumber) {
914 			case 1 :
915 				return position + 1;
916 			case 2:
917 				return position - startLineIndexes[0];
918 			default:
919 				int line = lineNumber - 2;
920 	    		int length = startLineIndexes.length;
921 	    		if (line >= length) {
922 	    			return position - startLineIndexes[length - 1];
923 	    		}
924 	    		return position - startLineIndexes[line];
925 		}
926 	}
927 
928 	/**
929 	 * Converts a boolean value into Boolean.
930 	 * @param bool The boolean to convert
931 	 * @return The corresponding Boolean object (TRUE or FALSE).
932 	 */
toBoolean(boolean bool)933 	public static Boolean toBoolean(boolean bool) {
934 		if (bool) {
935 			return Boolean.TRUE;
936 		} else {
937 			return Boolean.FALSE;
938 		}
939 	}
940 	/**
941 	 * Converts an array of Objects into String.
942 	 */
toString(Object[] objects)943 	public static String toString(Object[] objects) {
944 		return toString(objects,
945 			new Displayable(){
946 				public String displayString(Object o) {
947 					if (o == null) return "null"; //$NON-NLS-1$
948 					return o.toString();
949 				}
950 			});
951 	}
952 
953 	/**
954 	 * Converts an array of Objects into String.
955 	 */
956 	public static String toString(Object[] objects, Displayable renderer) {
957 		if (objects == null) return ""; //$NON-NLS-1$
958 		StringBuffer buffer = new StringBuffer(10);
959 		for (int i = 0; i < objects.length; i++){
960 			if (i > 0) buffer.append(", "); //$NON-NLS-1$
961 			buffer.append(renderer.displayString(objects[i]));
962 		}
963 		return buffer.toString();
964 	}
965 
966 	/**
967 	 * outputPath is formed like:
968 	 *	   c:\temp\ the last character is a file separator
969 	 * relativeFileName is formed like:
970 	 *     java\lang\String.class
971 	 * @param generatePackagesStructure a flag to know if the packages structure has to be generated.
972 	 * @param outputPath the given output directory
973 	 * @param relativeFileName the given relative file name
974 	 * @param classFile the given classFile to write
975 	 *
976 	 */
977 	public static void writeToDisk(boolean generatePackagesStructure, String outputPath, String relativeFileName, ClassFile classFile) throws IOException {
978 		FileOutputStream file = getFileOutputStream(generatePackagesStructure, outputPath, relativeFileName);
979 		/* use java.nio to write
980 		if (true) {
981 			FileChannel ch = file.getChannel();
982 			try {
983 				ByteBuffer buffer = ByteBuffer.allocate(classFile.headerOffset + classFile.contentsOffset);
984 				buffer.put(classFile.header, 0, classFile.headerOffset);
985 				buffer.put(classFile.contents, 0, classFile.contentsOffset);
986 				buffer.flip();
987 				while (true) {
988 					if (ch.write(buffer) == 0) break;
989 				}
990 			} finally {
991 				ch.close();
992 			}
993 			return;
994 		}
995 		*/
996 		BufferedOutputStream output = new BufferedOutputStream(file, DEFAULT_WRITING_SIZE);
997 //		BufferedOutputStream output = new BufferedOutputStream(file);
998 		try {
999 			// if no IOException occured, output cannot be null
1000 			output.write(classFile.header, 0, classFile.headerOffset);
1001 			output.write(classFile.contents, 0, classFile.contentsOffset);
1002 			output.flush();
1003 		} catch(IOException e) {
1004 			throw e;
1005 		} finally {
1006 			output.close();
1007 		}
1008 	}
1009 	public static void recordNestedType(ClassFile classFile, TypeBinding typeBinding) {
1010 		if (classFile.visitedTypes == null) {
1011 			classFile.visitedTypes = new HashSet(3);
1012 		} else if (classFile.visitedTypes.contains(typeBinding)) {
1013 			// type is already visited
1014 			return;
1015 		}
1016 		classFile.visitedTypes.add(typeBinding);
1017 		if (typeBinding.isParameterizedType()
1018 				&& ((typeBinding.tagBits & TagBits.ContainsNestedTypeReferences) != 0)) {
1019 			ParameterizedTypeBinding parameterizedTypeBinding = (ParameterizedTypeBinding) typeBinding;
1020 			ReferenceBinding genericType = parameterizedTypeBinding.genericType();
1021 			if ((genericType.tagBits & TagBits.ContainsNestedTypeReferences) != 0) {
1022 				recordNestedType(classFile, genericType);
1023 			}
1024 			TypeBinding[] arguments = parameterizedTypeBinding.arguments;
1025 			if (arguments != null) {
1026 				for (int j = 0, max2 = arguments.length; j < max2; j++) {
1027 					TypeBinding argument = arguments[j];
1028 					if (argument.isWildcard()) {
1029 						WildcardBinding wildcardBinding = (WildcardBinding) argument;
1030 						TypeBinding bound = wildcardBinding.bound;
1031 						if (bound != null
1032 								&& ((bound.tagBits & TagBits.ContainsNestedTypeReferences) != 0)) {
1033 							recordNestedType(classFile, bound);
1034 						}
1035 						ReferenceBinding superclass = wildcardBinding.superclass();
1036 						if (superclass != null
1037 								&& ((superclass.tagBits & TagBits.ContainsNestedTypeReferences) != 0)) {
1038 							recordNestedType(classFile, superclass);
1039 						}
1040 						ReferenceBinding[] superInterfaces = wildcardBinding.superInterfaces();
1041 						if (superInterfaces != null) {
1042 							for (int k = 0, max3 =  superInterfaces.length; k < max3; k++) {
1043 								ReferenceBinding superInterface = superInterfaces[k];
1044 								if ((superInterface.tagBits & TagBits.ContainsNestedTypeReferences) != 0) {
1045 									recordNestedType(classFile, superInterface);
1046 								}
1047 							}
1048 						}
1049 					} else if ((argument.tagBits & TagBits.ContainsNestedTypeReferences) != 0) {
1050 						recordNestedType(classFile, argument);
1051 					}
1052 				}
1053 			}
1054 		} else if (typeBinding.isTypeVariable()
1055 				&& ((typeBinding.tagBits & TagBits.ContainsNestedTypeReferences) != 0)) {
1056 			TypeVariableBinding typeVariableBinding = (TypeVariableBinding) typeBinding;
1057 			TypeBinding upperBound = typeVariableBinding.upperBound();
1058 			if (upperBound != null && ((upperBound.tagBits & TagBits.ContainsNestedTypeReferences) != 0)) {
1059 				recordNestedType(classFile, upperBound);
1060 			}
1061 			TypeBinding[] upperBounds = typeVariableBinding.otherUpperBounds();
1062 			if (upperBounds != null) {
1063 				for (int k = 0, max3 =  upperBounds.length; k < max3; k++) {
1064 					TypeBinding otherUpperBound = upperBounds[k];
1065 					if ((otherUpperBound.tagBits & TagBits.ContainsNestedTypeReferences) != 0) {
1066 						recordNestedType(classFile, otherUpperBound);
1067 					}
1068 				}
1069 			}
1070 		} else if (typeBinding.isNestedType()) {
1071 			classFile.recordInnerClasses(typeBinding);
1072 		}
1073 	}
1074 	/*
1075 	 * External API
1076 	 */
1077 	public static File getJavaHome() {
1078 		String javaHome = System.getProperty("java.home");//$NON-NLS-1$
1079 		if (javaHome != null) {
1080 			File javaHomeFile = new File(javaHome);
1081 			if (javaHomeFile.exists()) {
1082 				return javaHomeFile;
1083 			}
1084 		}
1085 		return null;
1086 	}
1087 
1088 	public static void collectRunningVMBootclasspath(List bootclasspaths) {
1089 		/* no bootclasspath specified
1090 		 * we can try to retrieve the default librairies of the VM used to run
1091 		 * the batch compiler
1092 		 */
1093 		String javaversion = System.getProperty("java.version");//$NON-NLS-1$
1094 		if (javaversion != null && javaversion.equalsIgnoreCase("1.1.8")) { //$NON-NLS-1$
1095 			throw new IllegalStateException();
1096 		}
1097 
1098 		/*
1099 		 * Handle >= JDK 1.2.2 settings: retrieve the bootclasspath
1100 		 */
1101 		// check bootclasspath properties for Sun, JRockit and Harmony VMs
1102 		String bootclasspathProperty = System.getProperty("sun.boot.class.path"); //$NON-NLS-1$
1103 		if ((bootclasspathProperty == null) || (bootclasspathProperty.length() == 0)) {
1104 			// IBM J9 VMs
1105 			bootclasspathProperty = System.getProperty("vm.boot.class.path"); //$NON-NLS-1$
1106 			if ((bootclasspathProperty == null) || (bootclasspathProperty.length() == 0)) {
1107 				// Harmony using IBM VME
1108 				bootclasspathProperty = System.getProperty("org.apache.harmony.boot.class.path"); //$NON-NLS-1$
1109 			}
1110 		}
1111 		if ((bootclasspathProperty != null) && (bootclasspathProperty.length() != 0)) {
1112 			StringTokenizer tokenizer = new StringTokenizer(bootclasspathProperty, File.pathSeparator);
1113 			String token;
1114 			while (tokenizer.hasMoreTokens()) {
1115 				token = tokenizer.nextToken();
1116 				FileSystem.Classpath currentClasspath = FileSystem.getClasspath(token, null, null);
1117 				if (currentClasspath != null) {
1118 					bootclasspaths.add(currentClasspath);
1119 				}
1120 			}
1121 		} else {
1122 			// try to get all jars inside the lib folder of the java home
1123 			final File javaHome = getJavaHome();
1124 			if (javaHome != null) {
1125 				File[] directoriesToCheck = null;
1126 				if (System.getProperty("os.name").startsWith("Mac")) {//$NON-NLS-1$//$NON-NLS-2$
1127 					directoriesToCheck = new File[] {
1128 						new File(javaHome, "../Classes"), //$NON-NLS-1$
1129 					};
1130 				} else {
1131 					// fall back to try to retrieve them out of the lib directory
1132 					directoriesToCheck = new File[] {
1133 						new File(javaHome, "lib") //$NON-NLS-1$
1134 					};
1135 				}
1136 				File[][] systemLibrariesJars = Main.getLibrariesFiles(directoriesToCheck);
1137 				if (systemLibrariesJars != null) {
1138 					for (int i = 0, max = systemLibrariesJars.length; i < max; i++) {
1139 						File[] current = systemLibrariesJars[i];
1140 						if (current != null) {
1141 							for (int j = 0, max2 = current.length; j < max2; j++) {
1142 								FileSystem.Classpath classpath =
1143 									FileSystem.getClasspath(current[j].getAbsolutePath(),
1144 										null, false, null, null);
1145 								if (classpath != null) {
1146 									bootclasspaths.add(classpath);
1147 								}
1148 							}
1149 						}
1150 					}
1151 				}
1152 			}
1153 		}
1154 	}
1155 	public static int getParameterCount(char[] methodSignature) {
1156 		try {
1157 			int count = 0;
1158 			int i = CharOperation.indexOf(C_PARAM_START, methodSignature);
1159 			if (i < 0) {
1160 				throw new IllegalArgumentException();
1161 			} else {
1162 				i++;
1163 			}
1164 			for (;;) {
1165 				if (methodSignature[i] == C_PARAM_END) {
1166 					return count;
1167 				}
1168 				int e= Util.scanTypeSignature(methodSignature, i);
1169 				if (e < 0) {
1170 					throw new IllegalArgumentException();
1171 				} else {
1172 					i = e + 1;
1173 				}
1174 				count++;
1175 			}
1176 		} catch (ArrayIndexOutOfBoundsException e) {
1177 			throw new IllegalArgumentException();
1178 		}
1179 	}
1180 
1181 	/**
1182 	 * Scans the given string for a type signature starting at the given index
1183 	 * and returns the index of the last character.
1184 	 * <pre>
1185 	 * TypeSignature:
1186 	 *  |  BaseTypeSignature
1187 	 *  |  ArrayTypeSignature
1188 	 *  |  ClassTypeSignature
1189 	 *  |  TypeVariableSignature
1190 	 * </pre>
1191 	 *
1192 	 * @param string the signature string
1193 	 * @param start the 0-based character index of the first character
1194 	 * @return the 0-based character index of the last character
1195 	 * @exception IllegalArgumentException if this is not a type signature
1196 	 */
1197 	public static int scanTypeSignature(char[] string, int start) {
1198 		// need a minimum 1 char
1199 		if (start >= string.length) {
1200 			throw new IllegalArgumentException();
1201 		}
1202 		char c = string[start];
1203 		switch (c) {
1204 			case C_ARRAY :
1205 				return scanArrayTypeSignature(string, start);
1206 			case C_RESOLVED :
1207 			case C_UNRESOLVED :
1208 				return scanClassTypeSignature(string, start);
1209 			case C_TYPE_VARIABLE :
1210 				return scanTypeVariableSignature(string, start);
1211 			case C_BOOLEAN :
1212 			case C_BYTE :
1213 			case C_CHAR :
1214 			case C_DOUBLE :
1215 			case C_FLOAT :
1216 			case C_INT :
1217 			case C_LONG :
1218 			case C_SHORT :
1219 			case C_VOID :
1220 				return scanBaseTypeSignature(string, start);
1221 			case C_CAPTURE :
1222 				return scanCaptureTypeSignature(string, start);
1223 			case C_EXTENDS:
1224 			case C_SUPER:
1225 			case C_STAR:
1226 				return scanTypeBoundSignature(string, start);
1227 			default :
1228 				throw new IllegalArgumentException();
1229 		}
1230 	}
1231 
1232 	/**
1233 	 * Scans the given string for a base type signature starting at the given index
1234 	 * and returns the index of the last character.
1235 	 * <pre>
1236 	 * BaseTypeSignature:
1237 	 *     <b>B</b> | <b>C</b> | <b>D</b> | <b>F</b> | <b>I</b>
1238 	 *   | <b>J</b> | <b>S</b> | <b>V</b> | <b>Z</b>
1239 	 * </pre>
1240 	 * Note that although the base type "V" is only allowed in method return types,
1241 	 * there is no syntactic ambiguity. This method will accept them anywhere
1242 	 * without complaint.
1243 	 *
1244 	 * @param string the signature string
1245 	 * @param start the 0-based character index of the first character
1246 	 * @return the 0-based character index of the last character
1247 	 * @exception IllegalArgumentException if this is not a base type signature
1248 	 */
1249 	public static int scanBaseTypeSignature(char[] string, int start) {
1250 		// need a minimum 1 char
1251 		if (start >= string.length) {
1252 			throw new IllegalArgumentException();
1253 		}
1254 		char c = string[start];
1255 		if ("BCDFIJSVZ".indexOf(c) >= 0) { //$NON-NLS-1$
1256 			return start;
1257 		} else {
1258 			throw new IllegalArgumentException();
1259 		}
1260 	}
1261 
1262 	/**
1263 	 * Scans the given string for an array type signature starting at the given
1264 	 * index and returns the index of the last character.
1265 	 * <pre>
1266 	 * ArrayTypeSignature:
1267 	 *     <b>[</b> TypeSignature
1268 	 * </pre>
1269 	 *
1270 	 * @param string the signature string
1271 	 * @param start the 0-based character index of the first character
1272 	 * @return the 0-based character index of the last character
1273 	 * @exception IllegalArgumentException if this is not an array type signature
1274 	 */
1275 	public static int scanArrayTypeSignature(char[] string, int start) {
1276 		int length = string.length;
1277 		// need a minimum 2 char
1278 		if (start >= length - 1) {
1279 			throw new IllegalArgumentException();
1280 		}
1281 		char c = string[start];
1282 		if (c != C_ARRAY) {
1283 			throw new IllegalArgumentException();
1284 		}
1285 
1286 		c = string[++start];
1287 		while(c == C_ARRAY) {
1288 			// need a minimum 2 char
1289 			if (start >= length - 1) {
1290 				throw new IllegalArgumentException();
1291 			}
1292 			c = string[++start];
1293 		}
1294 		return scanTypeSignature(string, start);
1295 	}
1296 
1297 	/**
1298 	 * Scans the given string for a capture of a wildcard type signature starting at the given
1299 	 * index and returns the index of the last character.
1300 	 * <pre>
1301 	 * CaptureTypeSignature:
1302 	 *     <b>!</b> TypeBoundSignature
1303 	 * </pre>
1304 	 *
1305 	 * @param string the signature string
1306 	 * @param start the 0-based character index of the first character
1307 	 * @return the 0-based character index of the last character
1308 	 * @exception IllegalArgumentException if this is not a capture type signature
1309 	 */
1310 	public static int scanCaptureTypeSignature(char[] string, int start) {
1311 		// need a minimum 2 char
1312 		if (start >= string.length - 1) {
1313 			throw new IllegalArgumentException();
1314 		}
1315 		char c = string[start];
1316 		if (c != C_CAPTURE) {
1317 			throw new IllegalArgumentException();
1318 		}
1319 		return scanTypeBoundSignature(string, start + 1);
1320 	}
1321 
1322 	/**
1323 	 * Scans the given string for a type variable signature starting at the given
1324 	 * index and returns the index of the last character.
1325 	 * <pre>
1326 	 * TypeVariableSignature:
1327 	 *     <b>T</b> Identifier <b>;</b>
1328 	 * </pre>
1329 	 *
1330 	 * @param string the signature string
1331 	 * @param start the 0-based character index of the first character
1332 	 * @return the 0-based character index of the last character
1333 	 * @exception IllegalArgumentException if this is not a type variable signature
1334 	 */
1335 	public static int scanTypeVariableSignature(char[] string, int start) {
1336 		// need a minimum 3 chars "Tx;"
1337 		if (start >= string.length - 2) {
1338 			throw new IllegalArgumentException();
1339 		}
1340 		// must start in "T"
1341 		char c = string[start];
1342 		if (c != C_TYPE_VARIABLE) {
1343 			throw new IllegalArgumentException();
1344 		}
1345 		int id = scanIdentifier(string, start + 1);
1346 		c = string[id + 1];
1347 		if (c == C_SEMICOLON) {
1348 			return id + 1;
1349 		} else {
1350 			throw new IllegalArgumentException();
1351 		}
1352 	}
1353 
1354 	/**
1355 	 * Scans the given string for an identifier starting at the given
1356 	 * index and returns the index of the last character.
1357 	 * Stop characters are: ";", ":", "&lt;", "&gt;", "/", ".".
1358 	 *
1359 	 * @param string the signature string
1360 	 * @param start the 0-based character index of the first character
1361 	 * @return the 0-based character index of the last character
1362 	 * @exception IllegalArgumentException if this is not an identifier
1363 	 */
1364 	public static int scanIdentifier(char[] string, int start) {
1365 		// need a minimum 1 char
1366 		if (start >= string.length) {
1367 			throw new IllegalArgumentException();
1368 		}
1369 		int p = start;
1370 		while (true) {
1371 			char c = string[p];
1372 			if (c == '<' || c == '>' || c == ':' || c == ';' || c == '.' || c == '/') {
1373 				return p - 1;
1374 			}
1375 			p++;
1376 			if (p == string.length) {
1377 				return p - 1;
1378 			}
1379 		}
1380 	}
1381 
1382 	/**
1383 	 * Scans the given string for a class type signature starting at the given
1384 	 * index and returns the index of the last character.
1385 	 * <pre>
1386 	 * ClassTypeSignature:
1387 	 *     { <b>L</b> | <b>Q</b> } Identifier
1388 	 *           { { <b>/</b> | <b>.</b> Identifier [ <b>&lt;</b> TypeArgumentSignature* <b>&gt;</b> ] }
1389 	 *           <b>;</b>
1390 	 * </pre>
1391 	 * Note that although all "/"-identifiers most come before "."-identifiers,
1392 	 * there is no syntactic ambiguity. This method will accept them without
1393 	 * complaint.
1394 	 *
1395 	 * @param string the signature string
1396 	 * @param start the 0-based character index of the first character
1397 	 * @return the 0-based character index of the last character
1398 	 * @exception IllegalArgumentException if this is not a class type signature
1399 	 */
1400 	public static int scanClassTypeSignature(char[] string, int start) {
1401 		// need a minimum 3 chars "Lx;"
1402 		if (start >= string.length - 2) {
1403 			throw new IllegalArgumentException();
1404 		}
1405 		// must start in "L" or "Q"
1406 		char c = string[start];
1407 		if (c != C_RESOLVED && c != C_UNRESOLVED) {
1408 			return -1;
1409 		}
1410 		int p = start + 1;
1411 		while (true) {
1412 			if (p >= string.length) {
1413 				throw new IllegalArgumentException();
1414 			}
1415 			c = string[p];
1416 			if (c == C_SEMICOLON) {
1417 				// all done
1418 				return p;
1419 			} else if (c == C_GENERIC_START) {
1420 				int e = scanTypeArgumentSignatures(string, p);
1421 				p = e;
1422 			} else if (c == C_DOT || c == '/') {
1423 				int id = scanIdentifier(string, p + 1);
1424 				p = id;
1425 			}
1426 			p++;
1427 		}
1428 	}
1429 
1430 	/**
1431 	 * Scans the given string for a type bound signature starting at the given
1432 	 * index and returns the index of the last character.
1433 	 * <pre>
1434 	 * TypeBoundSignature:
1435 	 *     <b>[-+]</b> TypeSignature <b>;</b>
1436 	 *     <b>*</b></b>
1437 	 * </pre>
1438 	 *
1439 	 * @param string the signature string
1440 	 * @param start the 0-based character index of the first character
1441 	 * @return the 0-based character index of the last character
1442 	 * @exception IllegalArgumentException if this is not a type variable signature
1443 	 */
1444 	public static int scanTypeBoundSignature(char[] string, int start) {
1445 		// need a minimum 1 char for wildcard
1446 		if (start >= string.length) {
1447 			throw new IllegalArgumentException();
1448 		}
1449 		char c = string[start];
1450 		switch (c) {
1451 			case C_STAR :
1452 				return start;
1453 			case C_SUPER :
1454 			case C_EXTENDS :
1455 				// need a minimum 3 chars "+[I"
1456 				if (start >= string.length - 2) {
1457 					throw new IllegalArgumentException();
1458 				}
1459 				break;
1460 			default :
1461 				// must start in "+/-"
1462 					throw new IllegalArgumentException();
1463 
1464 		}
1465 		c = string[++start];
1466 		switch (c) {
1467 			case C_CAPTURE :
1468 				return scanCaptureTypeSignature(string, start);
1469 			case C_SUPER :
1470 			case C_EXTENDS :
1471 				return scanTypeBoundSignature(string, start);
1472 			case C_RESOLVED :
1473 			case C_UNRESOLVED :
1474 				return scanClassTypeSignature(string, start);
1475 			case C_TYPE_VARIABLE :
1476 				return scanTypeVariableSignature(string, start);
1477 			case C_ARRAY :
1478 				return scanArrayTypeSignature(string, start);
1479 			case C_STAR:
1480 				return start;
1481 			default:
1482 				throw new IllegalArgumentException();
1483 		}
1484 	}
1485 
1486 	/**
1487 	 * Scans the given string for a list of type argument signatures starting at
1488 	 * the given index and returns the index of the last character.
1489 	 * <pre>
1490 	 * TypeArgumentSignatures:
1491 	 *     <b>&lt;</b> TypeArgumentSignature* <b>&gt;</b>
1492 	 * </pre>
1493 	 * Note that although there is supposed to be at least one type argument, there
1494 	 * is no syntactic ambiguity if there are none. This method will accept zero
1495 	 * type argument signatures without complaint.
1496 	 *
1497 	 * @param string the signature string
1498 	 * @param start the 0-based character index of the first character
1499 	 * @return the 0-based character index of the last character
1500 	 * @exception IllegalArgumentException if this is not a list of type arguments
1501 	 * signatures
1502 	 */
1503 	public static int scanTypeArgumentSignatures(char[] string, int start) {
1504 		// need a minimum 2 char "<>"
1505 		if (start >= string.length - 1) {
1506 			throw new IllegalArgumentException();
1507 		}
1508 		char c = string[start];
1509 		if (c != C_GENERIC_START) {
1510 			throw new IllegalArgumentException();
1511 		}
1512 		int p = start + 1;
1513 		while (true) {
1514 			if (p >= string.length) {
1515 				throw new IllegalArgumentException();
1516 			}
1517 			c = string[p];
1518 			if (c == C_GENERIC_END) {
1519 				return p;
1520 			}
1521 			int e = scanTypeArgumentSignature(string, p);
1522 			p = e + 1;
1523 		}
1524 	}
1525 
1526 	/**
1527 	 * Scans the given string for a type argument signature starting at the given
1528 	 * index and returns the index of the last character.
1529 	 * <pre>
1530 	 * TypeArgumentSignature:
1531 	 *     <b>&#42;</b>
1532 	 *  |  <b>+</b> TypeSignature
1533 	 *  |  <b>-</b> TypeSignature
1534 	 *  |  TypeSignature
1535 	 * </pre>
1536 	 * Note that although base types are not allowed in type arguments, there is
1537 	 * no syntactic ambiguity. This method will accept them without complaint.
1538 	 *
1539 	 * @param string the signature string
1540 	 * @param start the 0-based character index of the first character
1541 	 * @return the 0-based character index of the last character
1542 	 * @exception IllegalArgumentException if this is not a type argument signature
1543 	 */
1544 	public static int scanTypeArgumentSignature(char[] string, int start) {
1545 		// need a minimum 1 char
1546 		if (start >= string.length) {
1547 			throw new IllegalArgumentException();
1548 		}
1549 		char c = string[start];
1550 		switch (c) {
1551 			case C_STAR :
1552 				return start;
1553 			case C_EXTENDS :
1554 			case C_SUPER :
1555 				return scanTypeBoundSignature(string, start);
1556 			default :
1557 				return scanTypeSignature(string, start);
1558 		}
1559 	}
1560 
1561 	public static boolean effectivelyEqual(Object [] one, Object [] two) {
1562 		if (one == two)
1563 			return true;
1564 		int oneLength = one == null ? 0 : one.length;
1565 		int twoLength = two == null ? 0 : two.length;
1566 		if (oneLength != twoLength)
1567 			return false;
1568 		if (oneLength == 0)
1569 			return true;
1570 		for (int i = 0; i < one.length; i++) {
1571 			if (one[i] != two[i])
1572 				return false;
1573 		}
1574 		return true;
1575 	}
1576 }