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