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>'<'</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>'>'</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: ";", ":", "<", ">", "/", ".". 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><</b> TypeArgumentSignature* <b>></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><</b> TypeArgumentSignature* <b>></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>*</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 }