/******************************************************************************* * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * daolaf@gmail.com - Contribution for bug 3292227 *******************************************************************************/ package org.eclipse.che.ide.ext.java.jdt.internal.compiler.util; import org.eclipse.che.ide.ext.java.jdt.core.compiler.CharOperation; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ClassFileConstants; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import java.util.Collection; import java.util.List; public class Util implements SuffixConstants { /** Character constant indicating the primitive type boolean in a signature. Value is <code>'Z'</code>. */ public static final char C_BOOLEAN = 'Z'; /** Character constant indicating the primitive type byte in a signature. Value is <code>'B'</code>. */ public static final char C_BYTE = 'B'; /** Character constant indicating the primitive type char in a signature. Value is <code>'C'</code>. */ public static final char C_CHAR = 'C'; /** Character constant indicating the primitive type double in a signature. Value is <code>'D'</code>. */ public static final char C_DOUBLE = 'D'; /** Character constant indicating the primitive type float in a signature. Value is <code>'F'</code>. */ public static final char C_FLOAT = 'F'; /** Character constant indicating the primitive type int in a signature. Value is <code>'I'</code>. */ public static final char C_INT = 'I'; /** Character constant indicating the semicolon in a signature. Value is <code>';'</code>. */ public static final char C_SEMICOLON = ';'; /** * Character constant indicating the colon in a signature. Value is <code>':'</code>. * * @since 3.0 */ public static final char C_COLON = ':'; /** Character constant indicating the primitive type long in a signature. Value is <code>'J'</code>. */ public static final char C_LONG = 'J'; /** Character constant indicating the primitive type short in a signature. Value is <code>'S'</code>. */ public static final char C_SHORT = 'S'; /** Character constant indicating result type void in a signature. Value is <code>'V'</code>. */ public static final char C_VOID = 'V'; /** * Character constant indicating the start of a resolved type variable in a signature. Value is <code>'T'</code>. * * @since 3.0 */ public static final char C_TYPE_VARIABLE = 'T'; /** * Character constant indicating an unbound wildcard type argument in a signature. Value is <code>'*'</code>. * * @since 3.0 */ public static final char C_STAR = '*'; /** * Character constant indicating an exception in a signature. Value is <code>'^'</code>. * * @since 3.1 */ public static final char C_EXCEPTION_START = '^'; /** * Character constant indicating a bound wildcard type argument in a signature with extends clause. Value is <code>'+'</code>. * * @since 3.1 */ public static final char C_EXTENDS = '+'; /** * Character constant indicating a bound wildcard type argument in a signature with super clause. Value is <code>'-'</code>. * * @since 3.1 */ public static final char C_SUPER = '-'; /** Character constant indicating the dot in a signature. Value is <code>'.'</code>. */ public static final char C_DOT = '.'; /** Character constant indicating the dollar in a signature. Value is <code>'$'</code>. */ public static final char C_DOLLAR = '$'; /** Character constant indicating an array type in a signature. Value is <code>'['</code>. */ public static final char C_ARRAY = '['; /** Character constant indicating the start of a resolved, named type in a signature. Value is <code>'L'</code>. */ public static final char C_RESOLVED = 'L'; /** Character constant indicating the start of an unresolved, named type in a signature. Value is <code>'Q'</code>. */ public static final char C_UNRESOLVED = 'Q'; /** Character constant indicating the end of a named type in a signature. Value is <code>';'</code>. */ public static final char C_NAME_END = ';'; /** Character constant indicating the start of a parameter type list in a signature. Value is <code>'('</code>. */ public static final char C_PARAM_START = '('; /** Character constant indicating the end of a parameter type list in a signature. Value is <code>')'</code>. */ public static final char C_PARAM_END = ')'; /** * Character constant indicating the start of a formal type parameter (or type argument) list in a signature. Value is * <code>'<'</code>. * * @since 3.0 */ public static final char C_GENERIC_START = '<'; /** * Character constant indicating the end of a generic type list in a signature. Value is <code>'>'</code>. * * @since 3.0 */ public static final char C_GENERIC_END = '>'; /** * Character constant indicating a capture of a wildcard type in a signature. Value is <code>'!'</code>. * * @since 3.1 */ public static final char C_CAPTURE = '!'; public interface Displayable { String displayString(Object o); } public final static String UTF_8 = "UTF-8"; //$NON-NLS-1$ public static final String LINE_SEPARATOR = "\n";//$NON-NLS-1$ public static final String EMPTY_STRING = new String(CharOperation.NO_CHAR); public static final int[] EMPTY_INT_ARRAY = new int[0]; // /** // * Build all the directories and subdirectories corresponding to the packages names // * into the directory specified in parameters. // * // * outputPath is formed like: // * c:\temp\ the last character is a file separator // * relativeFileName is formed like: // * java\lang\String.class * // * // * @param outputPath java.lang.String // * @param relativeFileName java.lang.String // * @return java.lang.String // */ // public static String buildAllDirectoriesInto(String outputPath, String relativeFileName) throws IOException { // char fileSeparatorChar = File.separatorChar; // String fileSeparator = File.separator; // File f; // outputPath = outputPath.replace('/', fileSeparatorChar); // // these could be optimized out if we normalized paths once and for // // all // relativeFileName = relativeFileName.replace('/', fileSeparatorChar); // String outputDirPath, fileName; // int separatorIndex = relativeFileName.lastIndexOf(fileSeparatorChar); // if (separatorIndex == -1) { // if (outputPath.endsWith(fileSeparator)) { // outputDirPath = outputPath.substring(0, outputPath.length() - 1); // fileName = outputPath + relativeFileName; // } else { // outputDirPath = outputPath; // fileName = outputPath + fileSeparator + relativeFileName; // } // } else { // if (outputPath.endsWith(fileSeparator)) { // outputDirPath = outputPath + // relativeFileName.substring(0, separatorIndex); // fileName = outputPath + relativeFileName; // } else { // outputDirPath = outputPath + fileSeparator + // relativeFileName.substring(0, separatorIndex); // fileName = outputPath + fileSeparator + relativeFileName; // } // } // f = new File(outputDirPath); // f.mkdirs(); // if (f.isDirectory()) { // return fileName; // } else { // // the directory creation failed for some reason - retry using // // a slower algorithm so as to refine the diagnostic // if (outputPath.endsWith(fileSeparator)) { // outputPath = outputPath.substring(0, outputPath.length() - 1); // } // f = new File(outputPath); // boolean checkFileType = false; // if (f.exists()) { // checkFileType = true; // pre-existed // } else { // // we have to create that directory // if (!f.mkdirs()) { // if (f.exists()) { // // someone else created f -- need to check its type // checkFileType = true; // } else { // // no one could create f -- complain // throw new IOException(Messages.bind( // Messages.output_notValidAll, f.getAbsolutePath())); // } // } // } // if (checkFileType) { // if (!f.isDirectory()) { // throw new IOException(Messages.bind( // Messages.output_isFile, f.getAbsolutePath())); // } // } // StringBuffer outDir = new StringBuffer(outputPath); // outDir.append(fileSeparator); // StringTokenizer tokenizer = // new StringTokenizer(relativeFileName, fileSeparator); // String token = tokenizer.nextToken(); // while (tokenizer.hasMoreTokens()) { // f = new File(outDir.append(token).append(fileSeparator).toString()); // checkFileType = false; // reset // if (f.exists()) { // checkFileType = true; // this is suboptimal, but it catches corner cases // // in which a regular file pre-exists // } else { // // we have to create that directory // if (!f.mkdir()) { // if (f.exists()) { // // someone else created f -- need to check its type // checkFileType = true; // } else { // // no one could create f -- complain // throw new IOException(Messages.bind( // Messages.output_notValid, // outDir.substring(outputPath.length() + 1, // outDir.length() - 1), // outputPath)); // } // } // } // if (checkFileType) { // if (!f.isDirectory()) { // throw new IOException(Messages.bind( // Messages.output_isFile, f.getAbsolutePath())); // } // } // token = tokenizer.nextToken(); // } // // token contains the last one // return outDir.append(token).toString(); // } // } // // /** // * Returns the given bytes as a char array using a given encoding (null means platform default). // */ // public static char[] bytesToChar(byte[] bytes, String encoding) throws IOException { // // return getInputStreamAsCharArray(new ByteArrayInputStream(bytes), bytes.length, encoding); // // } public static char[][] toArrays(Collection<String> strings) { char[][] arr = new char[strings.size()][]; int i=0; for(String str : strings) { arr[i++] = str.toCharArray(); } return arr; } /** Returns the outer most enclosing type's visibility for the given TypeDeclaration and visibility based on compiler options. */ public static int computeOuterMostVisibility(TypeDeclaration typeDeclaration, int visibility) { while (typeDeclaration != null) { switch (typeDeclaration.modifiers & ExtraCompilerModifiers.AccVisibilityMASK) { case ClassFileConstants.AccPrivate: visibility = ClassFileConstants.AccPrivate; break; case ClassFileConstants.AccDefault: if (visibility != ClassFileConstants.AccPrivate) { visibility = ClassFileConstants.AccDefault; } break; case ClassFileConstants.AccProtected: if (visibility == ClassFileConstants.AccPublic) { visibility = ClassFileConstants.AccProtected; } break; } typeDeclaration = typeDeclaration.enclosingType; } return visibility; } // /** // * Returns the contents of the given file as a byte array. // * @throws IOException if a problem occured reading the file. // */ // public static byte[] getFileByteContent(File file) throws IOException { // InputStream stream = null; // try { // stream = new BufferedInputStream(new FileInputStream(file)); // return getInputStreamAsByteArray(stream, (int) file.length()); // } finally { // if (stream != null) { // try { // stream.close(); // } catch (IOException e) { // // ignore // } // } // } // } // /** // * Returns the contents of the given file as a char array. // * When encoding is null, then the platform default one is used // * @throws IOException if a problem occured reading the file. // */ // public static char[] getFileCharContent(File file, String encoding) throws IOException { // InputStream stream = null; // try { // stream = new FileInputStream(file); // return getInputStreamAsCharArray(stream, (int) file.length(), encoding); // } finally { // if (stream != null) { // try { // stream.close(); // } catch (IOException e) { // // ignore // } // } // } // } // private static FileOutputStream getFileOutputStream(boolean generatePackagesStructure, String outputPath, String // relativeFileName) throws IOException { // if (generatePackagesStructure) { // return new FileOutputStream(new File(buildAllDirectoriesInto(outputPath, relativeFileName))); // } else { // String fileName = null; // char fileSeparatorChar = File.separatorChar; // String fileSeparator = File.separator; // // First we ensure that the outputPath exists // outputPath = outputPath.replace('/', fileSeparatorChar); // // To be able to pass the mkdirs() method we need to remove the extra file separator at the end of the outDir name // int indexOfPackageSeparator = relativeFileName.lastIndexOf(fileSeparatorChar); // if (indexOfPackageSeparator == -1) { // if (outputPath.endsWith(fileSeparator)) { // fileName = outputPath + relativeFileName; // } else { // fileName = outputPath + fileSeparator + relativeFileName; // } // } else { // int length = relativeFileName.length(); // if (outputPath.endsWith(fileSeparator)) { // fileName = outputPath + relativeFileName.substring(indexOfPackageSeparator + 1, length); // } else { // fileName = outputPath + fileSeparator + relativeFileName.substring(indexOfPackageSeparator + 1, length); // } // } // return new FileOutputStream(new File(fileName)); // } // } /* * NIO support to get input stream as byte array. Not used as with JDK 1.4.2 this support is slower than standard IO one... * Keep it as comment for future in case of next JDK versions improve performance in this area... public static byte[] * getInputStreamAsByteArray(FileInputStream stream, int length) throws IOException { FileChannel channel = * stream.getChannel(); int size = (int)channel.size(); if (length >= 0 && length < size) size = length; byte[] contents = new * byte[size]; ByteBuffer buffer = ByteBuffer.wrap(contents); channel.read(buffer); return contents; } */ // /** // * Returns the given input stream's contents as a byte array. // * If a length is specified (i.e. if length != -1), only length bytes // * are returned. Otherwise all bytes in the stream are returned. // * Note this doesn't close the stream. // * @throws IOException if a problem occured reading the stream. // */ // public static byte[] getInputStreamAsByteArray(InputStream stream, int length) // throws IOException { // byte[] contents; // if (length == -1) { // contents = new byte[0]; // int contentsLength = 0; // int amountRead = -1; // do { // int amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE); // read at least 8K // // // resize contents if needed // if (contentsLength + amountRequested > contents.length) { // System.arraycopy( // contents, // 0, // contents = new byte[contentsLength + amountRequested], // 0, // contentsLength); // } // // // read as many bytes as possible // amountRead = stream.read(contents, contentsLength, amountRequested); // // if (amountRead > 0) { // // remember length of contents // contentsLength += amountRead; // } // } while (amountRead != -1); // // // resize contents if necessary // if (contentsLength < contents.length) { // System.arraycopy( // contents, // 0, // contents = new byte[contentsLength], // 0, // contentsLength); // } // } else { // contents = new byte[length]; // int len = 0; // int readSize = 0; // while ((readSize != -1) && (len != length)) { // // See PR 1FMS89U // // We record first the read size. In this case len is the actual read size. // len += readSize; // readSize = stream.read(contents, len, length - len); // } // } // // return contents; // } /* * NIO support to get input stream as char array. Not used as with JDK 1.4.2 this support is slower than standard IO one... * Keep it as comment for future in case of next JDK versions improve performance in this area... public static char[] * getInputStreamAsCharArray(FileInputStream stream, int length, String encoding) throws IOException { FileChannel channel = * stream.getChannel(); int size = (int)channel.size(); if (length >= 0 && length < size) size = length; Charset charset = * encoding==null?systemCharset:Charset.forName(encoding); if (charset != null) { MappedByteBuffer bbuffer = * channel.valueMap(FileChannel.MapMode.READ_ONLY, 0, size); CharsetDecoder decoder = charset.newDecoder(); CharBuffer buffer = * decoder.decode(bbuffer); char[] contents = new char[buffer.limit()]; buffer.get(contents); return contents; } throw new * UnsupportedCharsetException(SYSTEM_FILE_ENCODING); } */ // /** // * Returns the given input stream's contents as a character array. // * If a length is specified (i.e. if length != -1), this represents the number of bytes in the stream. // * Note this doesn't close the stream. // * @throws IOException if a problem occured reading the stream. // */ // public static char[] getInputStreamAsCharArray(InputStream stream, int length, String encoding) // throws IOException { // BufferedReader reader = null; // try { // reader = encoding == null // ? new BufferedReader(new InputStreamReader(stream)) // : new BufferedReader(new InputStreamReader(stream, encoding)); // } catch (UnsupportedEncodingException e) { // // encoding is not supported // reader = new BufferedReader(new InputStreamReader(stream)); // } // char[] contents; // int totalRead = 0; // if (length == -1) { // contents = CharOperation.NO_CHAR; // } else { // // length is a good guess when the encoding produces less or the same amount of characters than the file length // contents = new char[length]; // best guess // } // // while (true) { // int amountRequested; // if (totalRead < length) { // // until known length is met, reuse same array sized eagerly // amountRequested = length - totalRead; // } else { // // reading beyond known length // int current = reader.read(); // if (current < 0) break; // // amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE); // read at least 8K // // // resize contents if needed // if (totalRead + 1 + amountRequested > contents.length) // System.arraycopy(contents, 0, contents = new char[totalRead + 1 + amountRequested], 0, totalRead); // // // add current character // contents[totalRead++] = (char) current; // coming from totalRead==length // } // // read as many chars as possible // int amountRead = reader.read(contents, totalRead, amountRequested); // if (amountRead < 0) break; // totalRead += amountRead; // } // // // Do not keep first character for UTF-8 BOM encoding // int start = 0; // if (totalRead > 0 && UTF_8.equals(encoding)) { // if (contents[0] == 0xFEFF) { // if BOM char then skip // totalRead--; // start = 1; // } // } // // // resize contents if necessary // if (totalRead < contents.length) // System.arraycopy(contents, start, contents = new char[totalRead], 0, totalRead); // // return contents; // } // /** * Returns a one line summary for an exception (extracted from its stacktrace: name + first frame) * * @param exception * @return one line summary for an exception */ public static String getExceptionSummary(Throwable exception) { // StringWriter stringWriter = new StringWriter(); // exception.printStackTrace(new PrintWriter(stringWriter)); // StringBuffer buffer = exception.p stringWriter.getBuffer(); StringBuffer exceptionBuffer = new StringBuffer(50); exceptionBuffer.append(exception.toString()); // TODO find way to use printStackTrace to fill buffer // only keep leading frame portion of the trace (i.e. line no. 2 from the stacktrace) // lookupLine2 : for (int i = 0, lineSep = 0, max = buffer.length(), line2Start = 0; i < max; i++) // { // switch (buffer.charAt(i)) // { // case '\n' : // case '\r' : // if (line2Start > 0) // { // exceptionBuffer.append(' ').append(buffer.substring(line2Start, i)); // break lookupLine2; // } // lineSep++; // break; // case ' ' : // case '\t' : // break; // default : // if (lineSep > 0) // { // line2Start = i; // lineSep = 0; // } // break; // } // } return exceptionBuffer.toString(); } // public static int getLineNumber(int position, int[] lineEnds, int g, int d) { if (lineEnds == null) return 1; if (d == -1) return 1; int m = g, start; while (g <= d) { m = g + (d - g) / 2; if (position < (start = lineEnds[m])) { d = m - 1; } else if (position > start) { g = m + 1; } else { return m + 1; } } if (position < lineEnds[m]) { return m + 1; } return m + 2; } // /** // * Returns the contents of the given zip entry as a byte array. // * @throws IOException if a problem occured reading the zip entry. // */ // public static byte[] getZipEntryByteContent(ZipEntry ze, ZipFile zip) // throws IOException { // // InputStream stream = null; // try { // InputStream inputStream = zip.getInputStream(ze); // if (inputStream == null) throw new IOException("Invalid zip entry name : " + ze.getName()); //$NON-NLS-1$ // stream = new BufferedInputStream(inputStream); // return getInputStreamAsByteArray(stream, (int) ze.getSize()); // } finally { // if (stream != null) { // try { // stream.close(); // } catch (IOException e) { // // ignore // } // } // } // } public static int hashCode(Object[] array) { int prime = 31; if (array == null) { return 0; } int result = 1; for (int index = 0; index < array.length; index++) { result = prime * result + (array[index] == null ? 0 : array[index].hashCode()); } return result; } // /** // * Returns whether the given name is potentially a zip archive file name // * (it has a file extension and it is not ".java" nor ".class") // */ // public final static boolean isPotentialZipArchive(String name) { // int lastDot = name.lastIndexOf('.'); // if (lastDot == -1) // return false; // no file extension, it cannot be a zip archive name // if (name.lastIndexOf(File.separatorChar) > lastDot) // return false; // dot was before the last file separator, it cannot be a zip archive name // int length = name.length(); // int extensionLength = length - lastDot - 1; // if (extensionLength == EXTENSION_java.length()) { // for (int i = extensionLength-1; i >=0; i--) { // if (Character.toLowerCase(name.charAt(length - extensionLength + i)) != EXTENSION_java.charAt(i)) { // break; // not a ".java" file, check ".class" file case below // } // if (i == 0) { // return false; // it is a ".java" file, it cannot be a zip archive name // } // } // } // if (extensionLength == EXTENSION_class.length()) { // for (int i = extensionLength-1; i >=0; i--) { // if (Character.toLowerCase(name.charAt(length - extensionLength + i)) != EXTENSION_class.charAt(i)) { // return true; // not a ".class" file, so this is a potential archive name // } // } // return false; // it is a ".class" file, it cannot be a zip archive name // } // return true; // it is neither a ".java" file nor a ".class" file, so this is a potential archive name // } /** Returns true iff str.toLowerCase().endsWith(".class") implementation is not creating extra strings. */ public final static boolean isClassFileName(char[] name) { int nameLength = name == null ? 0 : name.length; int suffixLength = SUFFIX_CLASS.length; if (nameLength < suffixLength) return false; for (int i = 0, offset = nameLength - suffixLength; i < suffixLength; i++) { char c = name[offset + i]; if (c != SUFFIX_class[i] && c != SUFFIX_CLASS[i]) return false; } return true; } /** Returns true iff str.toLowerCase().endsWith(".class") implementation is not creating extra strings. */ public final static boolean isClassFileName(String name) { int nameLength = name == null ? 0 : name.length(); int suffixLength = SUFFIX_CLASS.length; if (nameLength < suffixLength) return false; for (int i = 0; i < suffixLength; i++) { char c = name.charAt(nameLength - i - 1); int suffixIndex = suffixLength - i - 1; if (c != SUFFIX_class[suffixIndex] && c != SUFFIX_CLASS[suffixIndex]) return false; } return true; } /* * TODO (philippe) should consider promoting it to CharOperation Returns whether the given resource path matches one of the * inclusion/exclusion patterns. NOTE: should not be asked directly using pkg root pathes * @see IClasspathEntry#getInclusionPatterns * @see IClasspathEntry#getExclusionPatterns */ public final static boolean isExcluded(char[] path, char[][] inclusionPatterns, char[][] exclusionPatterns, boolean isFolderPath) { if (inclusionPatterns == null && exclusionPatterns == null) return false; inclusionCheck: if (inclusionPatterns != null) { for (int i = 0, length = inclusionPatterns.length; i < length; i++) { char[] pattern = inclusionPatterns[i]; char[] folderPattern = pattern; if (isFolderPath) { int lastSlash = CharOperation.lastIndexOf('/', pattern); if (lastSlash != -1 && lastSlash != pattern.length - 1) { // trailing slash -> adds '**' for free (see http://ant.apache // .org/manual/dirtasks.html) int star = CharOperation.indexOf('*', pattern, lastSlash); if ((star == -1 || star >= pattern.length - 1 || pattern[star + 1] != '*')) { folderPattern = CharOperation.subarray(pattern, 0, lastSlash); } } } if (CharOperation.pathMatch(folderPattern, path, true, '/')) { break inclusionCheck; } } return true; // never included } if (isFolderPath) { path = CharOperation.concat(path, new char[]{'*'}, '/'); } if (exclusionPatterns != null) { for (int i = 0, length = exclusionPatterns.length; i < length; i++) { if (CharOperation.pathMatch(exclusionPatterns[i], path, true, '/')) { return true; } } } return false; } /** Returns true iff str.toLowerCase().endsWith(".java") implementation is not creating extra strings. */ public final static boolean isJavaFileName(char[] name) { int nameLength = name == null ? 0 : name.length; int suffixLength = SUFFIX_JAVA.length; if (nameLength < suffixLength) return false; for (int i = 0, offset = nameLength - suffixLength; i < suffixLength; i++) { char c = name[offset + i]; if (c != SUFFIX_java[i] && c != SUFFIX_JAVA[i]) return false; } return true; } /** Returns true iff str.toLowerCase().endsWith(".java") implementation is not creating extra strings. */ public final static boolean isJavaFileName(String name) { int nameLength = name == null ? 0 : name.length(); int suffixLength = SUFFIX_JAVA.length; if (nameLength < suffixLength) return false; for (int i = 0; i < suffixLength; i++) { char c = name.charAt(nameLength - i - 1); int suffixIndex = suffixLength - i - 1; if (c != SUFFIX_java[suffixIndex] && c != SUFFIX_JAVA[suffixIndex]) return false; } return true; } public static void reverseQuickSort(char[][] list, int left, int right) { int original_left = left; int original_right = right; char[] mid = list[left + ((right - left) / 2)]; do { while (CharOperation.compareTo(list[left], mid) > 0) { left++; } while (CharOperation.compareTo(mid, list[right]) > 0) { right--; } if (left <= right) { char[] tmp = list[left]; list[left] = list[right]; list[right] = tmp; left++; right--; } } while (left <= right); if (original_left < right) { reverseQuickSort(list, original_left, right); } if (left < original_right) { reverseQuickSort(list, left, original_right); } } public static void reverseQuickSort(char[][] list, int left, int right, int[] result) { int original_left = left; int original_right = right; char[] mid = list[left + ((right - left) / 2)]; do { while (CharOperation.compareTo(list[left], mid) > 0) { left++; } while (CharOperation.compareTo(mid, list[right]) > 0) { right--; } if (left <= right) { char[] tmp = list[left]; list[left] = list[right]; list[right] = tmp; int temp = result[left]; result[left] = result[right]; result[right] = temp; left++; right--; } } while (left <= right); if (original_left < right) { reverseQuickSort(list, original_left, right, result); } if (left < original_right) { reverseQuickSort(list, left, original_right, result); } } /** INTERNAL USE-ONLY Search the column number corresponding to a specific position */ public static final int searchColumnNumber(int[] startLineIndexes, int lineNumber, int position) { switch (lineNumber) { case 1: return position + 1; case 2: return position - startLineIndexes[0]; default: int line = lineNumber - 2; int length = startLineIndexes.length; if (line >= length) { return position - startLineIndexes[length - 1]; } return position - startLineIndexes[line]; } } /** * Converts a boolean value into Boolean. * * @param bool * The boolean to convert * @return The corresponding Boolean object (TRUE or FALSE). */ public static Boolean toBoolean(boolean bool) { if (bool) { return Boolean.TRUE; } else { return Boolean.FALSE; } } /** Converts an array of Objects into String. */ public static String toString(Object[] objects) { return toString(objects, new Displayable() { public String displayString(Object o) { if (o == null) return "null"; //$NON-NLS-1$ return o.toString(); } }); } /** Converts an array of Objects into String. */ public static String toString(Object[] objects, Displayable renderer) { if (objects == null) return ""; //$NON-NLS-1$ StringBuffer buffer = new StringBuffer(10); for (int i = 0; i < objects.length; i++) { if (i > 0) buffer.append(", "); //$NON-NLS-1$ buffer.append(renderer.displayString(objects[i])); } return buffer.toString(); } // /** // * outputPath is formed like: // * c:\temp\ the last character is a file separator // * relativeFileName is formed like: // * java\lang\String.class // * @param generatePackagesStructure a flag to know if the packages structure has to be generated. // * @param outputPath the given output directory // * @param relativeFileName the given relative file name // * @param classFile the given classFile to write // * // */ // public static void writeToDisk(boolean generatePackagesStructure, String outputPath, String relativeFileName, ClassFile // classFile) throws IOException { // FileOutputStream file = getFileOutputStream(generatePackagesStructure, outputPath, relativeFileName); // /* use java.nio to write // if (true) { // FileChannel ch = file.getChannel(); // try { // ByteBuffer buffer = ByteBuffer.allocate(classFile.headerOffset + classFile.contentsOffset); // buffer.put(classFile.header, 0, classFile.headerOffset); // buffer.put(classFile.contents, 0, classFile.contentsOffset); // buffer.flip(); // while (true) { // if (ch.write(buffer) == 0) break; // } // } finally { // ch.close(); // } // return; // } // */ // BufferedOutputStream output = new BufferedOutputStream(file, DEFAULT_WRITING_SIZE); // // BufferedOutputStream output = new BufferedOutputStream(file); // try { // // if no IOException occured, output cannot be null // output.write(classFile.header, 0, classFile.headerOffset); // output.write(classFile.contents, 0, classFile.contentsOffset); // output.flush(); // } catch(IOException e) { // throw e; // } finally { // output.close(); // } // } // public static void recordNestedType(ClassFile classFile, TypeBinding typeBinding) { // if (classFile.visitedTypes == null) { // classFile.visitedTypes = new HashSet(3); // } else if (classFile.visitedTypes.contains(typeBinding)) { // // type is already visited // return; // } // classFile.visitedTypes.add(typeBinding); // if (typeBinding.isParameterizedType() // && ((typeBinding.tagBits & TagBits.ContainsNestedTypeReferences) != 0)) { // ParameterizedTypeBinding parameterizedTypeBinding = (ParameterizedTypeBinding) typeBinding; // ReferenceBinding genericType = parameterizedTypeBinding.genericType(); // if ((genericType.tagBits & TagBits.ContainsNestedTypeReferences) != 0) { // recordNestedType(classFile, genericType); // } // TypeBinding[] arguments = parameterizedTypeBinding.arguments; // if (arguments != null) { // for (int j = 0, max2 = arguments.length; j < max2; j++) { // TypeBinding argument = arguments[j]; // if (argument.isWildcard()) { // WildcardBinding wildcardBinding = (WildcardBinding) argument; // TypeBinding bound = wildcardBinding.bound; // if (bound != null // && ((bound.tagBits & TagBits.ContainsNestedTypeReferences) != 0)) { // recordNestedType(classFile, bound); // } // ReferenceBinding superclass = wildcardBinding.superclass(); // if (superclass != null // && ((superclass.tagBits & TagBits.ContainsNestedTypeReferences) != 0)) { // recordNestedType(classFile, superclass); // } // ReferenceBinding[] superInterfaces = wildcardBinding.superInterfaces(); // if (superInterfaces != null) { // for (int k = 0, max3 = superInterfaces.length; k < max3; k++) { // ReferenceBinding superInterface = superInterfaces[k]; // if ((superInterface.tagBits & TagBits.ContainsNestedTypeReferences) != 0) { // recordNestedType(classFile, superInterface); // } // } // } // } else if ((argument.tagBits & TagBits.ContainsNestedTypeReferences) != 0) { // recordNestedType(classFile, argument); // } // } // } // } else if (typeBinding.isTypeVariable() // && ((typeBinding.tagBits & TagBits.ContainsNestedTypeReferences) != 0)) { // TypeVariableBinding typeVariableBinding = (TypeVariableBinding) typeBinding; // TypeBinding upperBound = typeVariableBinding.upperBound(); // if (upperBound != null && ((upperBound.tagBits & TagBits.ContainsNestedTypeReferences) != 0)) { // recordNestedType(classFile, upperBound); // } // TypeBinding[] upperBounds = typeVariableBinding.otherUpperBounds(); // if (upperBounds != null) { // for (int k = 0, max3 = upperBounds.length; k < max3; k++) { // TypeBinding otherUpperBound = upperBounds[k]; // if ((otherUpperBound.tagBits & TagBits.ContainsNestedTypeReferences) != 0) { // recordNestedType(classFile, otherUpperBound); // } // } // } // } else if (typeBinding.isNestedType()) { // classFile.recordInnerClasses(typeBinding); // } // } /* // * External API // */ // public static File getJavaHome() { // String javaHome = System.getProperty("java.home");//$NON-NLS-1$ // if (javaHome != null) { // File javaHomeFile = new File(javaHome); // if (javaHomeFile.exists()) { // return javaHomeFile; // } // } // return null; // } public static void collectRunningVMBootclasspath(List bootclasspaths) { // /* no bootclasspath specified // * we can try to retrieve the default librairies of the VM used to run // * the batch compiler // */ // String javaversion = System.getProperty("java.version");//$NON-NLS-1$ // if (javaversion != null && javaversion.equalsIgnoreCase("1.1.8")) { //$NON-NLS-1$ // throw new IllegalStateException(); // } // // /* // * Handle >= JDK 1.2.2 settings: retrieve the bootclasspath // */ // // check bootclasspath properties for Sun, JRockit and Harmony VMs // String bootclasspathProperty = System.getProperty("sun.boot.class.path"); //$NON-NLS-1$ // if ((bootclasspathProperty == null) || (bootclasspathProperty.length() == 0)) { // // IBM J9 VMs // bootclasspathProperty = System.getProperty("vm.boot.class.path"); //$NON-NLS-1$ // if ((bootclasspathProperty == null) || (bootclasspathProperty.length() == 0)) { // // Harmony using IBM VME // bootclasspathProperty = System.getProperty("org.apache.harmony.boot.class.path"); //$NON-NLS-1$ // } // } // if ((bootclasspathProperty != null) && (bootclasspathProperty.length() != 0)) { // StringTokenizer tokenizer = new StringTokenizer(bootclasspathProperty, File.pathSeparator); // String token; // while (tokenizer.hasMoreTokens()) { // token = tokenizer.nextToken(); // FileSystem.Classpath currentClasspath = FileSystem.getClasspath(token, null, null); // if (currentClasspath != null) { // bootclasspaths.add(currentClasspath); // } // } // } else { // // try to get all jars inside the lib folder of the java home // final File javaHome = getJavaHome(); // if (javaHome != null) { // File[] directoriesToCheck = null; // if (System.getProperty("os.name").startsWith("Mac")) {//$NON-NLS-1$//$NON-NLS-2$ // directoriesToCheck = new File[] { // new File(javaHome, "../Classes"), //$NON-NLS-1$ // }; // } else { // // fall back to try to retrieve them out of the lib directory // directoriesToCheck = new File[] { // new File(javaHome, "lib") //$NON-NLS-1$ // }; // } // File[][] systemLibrariesJars = Main.getLibrariesFiles(directoriesToCheck); // if (systemLibrariesJars != null) { // for (int i = 0, max = systemLibrariesJars.length; i < max; i++) { // File[] current = systemLibrariesJars[i]; // if (current != null) { // for (int j = 0, max2 = current.length; j < max2; j++) { // FileSystem.Classpath classpath = // FileSystem.getClasspath(current[j].getAbsolutePath(), // null, false, null, null); // if (classpath != null) { // bootclasspaths.add(classpath); // } // } // } // } // } // } // } } public static int getParameterCount(char[] methodSignature) { try { int count = 0; int i = CharOperation.indexOf(C_PARAM_START, methodSignature); if (i < 0) { throw new IllegalArgumentException(); } else { i++; } for (; ; ) { if (methodSignature[i] == C_PARAM_END) { return count; } int e = Util.scanTypeSignature(methodSignature, i); if (e < 0) { throw new IllegalArgumentException(); } else { i = e + 1; } count++; } } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException(); } } /** * Scans the given string for a type signature starting at the given index and returns the index of the last character. * <p/> * <pre> * TypeSignature: * | BaseTypeSignature * | ArrayTypeSignature * | ClassTypeSignature * | TypeVariableSignature * </pre> * * @param string * the signature string * @param start * the 0-based character index of the first character * @return the 0-based character index of the last character * @throws IllegalArgumentException * if this is not a type signature */ public static int scanTypeSignature(char[] string, int start) { // need a minimum 1 char if (start >= string.length) { throw new IllegalArgumentException(); } char c = string[start]; switch (c) { case C_ARRAY: return scanArrayTypeSignature(string, start); case C_RESOLVED: case C_UNRESOLVED: return scanClassTypeSignature(string, start); case C_TYPE_VARIABLE: return scanTypeVariableSignature(string, start); case C_BOOLEAN: case C_BYTE: case C_CHAR: case C_DOUBLE: case C_FLOAT: case C_INT: case C_LONG: case C_SHORT: case C_VOID: return scanBaseTypeSignature(string, start); case C_CAPTURE: return scanCaptureTypeSignature(string, start); case C_EXTENDS: case C_SUPER: case C_STAR: return scanTypeBoundSignature(string, start); default: throw new IllegalArgumentException(); } } /** * Scans the given string for a base type signature starting at the given index and returns the index of the last character. * <p/> * <pre> * BaseTypeSignature: * <b>B</b> | <b>C</b> | <b>D</b> | <b>F</b> | <b>I</b> * | <b>J</b> | <b>S</b> | <b>V</b> | <b>Z</b> * </pre> * <p/> * Note that although the base type "V" is only allowed in method return types, there is no syntactic ambiguity. This method * will accept them anywhere without complaint. * * @param string * the signature string * @param start * the 0-based character index of the first character * @return the 0-based character index of the last character * @throws IllegalArgumentException * if this is not a base type signature */ public static int scanBaseTypeSignature(char[] string, int start) { // need a minimum 1 char if (start >= string.length) { throw new IllegalArgumentException(); } char c = string[start]; if ("BCDFIJSVZ".indexOf(c) >= 0) { //$NON-NLS-1$ return start; } else { throw new IllegalArgumentException(); } } /** * Scans the given string for an array type signature starting at the given index and returns the index of the last character. * <p/> * <pre> * ArrayTypeSignature: * <b>[</b> TypeSignature * </pre> * * @param string * the signature string * @param start * the 0-based character index of the first character * @return the 0-based character index of the last character * @throws IllegalArgumentException * if this is not an array type signature */ public static int scanArrayTypeSignature(char[] string, int start) { int length = string.length; // need a minimum 2 char if (start >= length - 1) { throw new IllegalArgumentException(); } char c = string[start]; if (c != C_ARRAY) { throw new IllegalArgumentException(); } c = string[++start]; while (c == C_ARRAY) { // need a minimum 2 char if (start >= length - 1) { throw new IllegalArgumentException(); } c = string[++start]; } return scanTypeSignature(string, start); } /** * Scans the given string for a capture of a wildcard type signature starting at the given index and returns the index of the * last character. * <p/> * <pre> * CaptureTypeSignature: * <b>!</b> TypeBoundSignature * </pre> * * @param string * the signature string * @param start * the 0-based character index of the first character * @return the 0-based character index of the last character * @throws IllegalArgumentException * if this is not a capture type signature */ public static int scanCaptureTypeSignature(char[] string, int start) { // need a minimum 2 char if (start >= string.length - 1) { throw new IllegalArgumentException(); } char c = string[start]; if (c != C_CAPTURE) { throw new IllegalArgumentException(); } return scanTypeBoundSignature(string, start + 1); } /** * Scans the given string for a type variable signature starting at the given index and returns the index of the last * character. * <p/> * <pre> * TypeVariableSignature: * <b>T</b> Identifier <b>;</b> * </pre> * * @param string * the signature string * @param start * the 0-based character index of the first character * @return the 0-based character index of the last character * @throws IllegalArgumentException * if this is not a type variable signature */ public static int scanTypeVariableSignature(char[] string, int start) { // need a minimum 3 chars "Tx;" if (start >= string.length - 2) { throw new IllegalArgumentException(); } // must start in "T" char c = string[start]; if (c != C_TYPE_VARIABLE) { throw new IllegalArgumentException(); } int id = scanIdentifier(string, start + 1); c = string[id + 1]; if (c == C_SEMICOLON) { return id + 1; } else { throw new IllegalArgumentException(); } } /** * Scans the given string for an identifier starting at the given index and returns the index of the last character. Stop * characters are: ";", ":", "<", ">", "/", ".". * * @param string * the signature string * @param start * the 0-based character index of the first character * @return the 0-based character index of the last character * @throws IllegalArgumentException * if this is not an identifier */ public static int scanIdentifier(char[] string, int start) { // need a minimum 1 char if (start >= string.length) { throw new IllegalArgumentException(); } int p = start; while (true) { char c = string[p]; if (c == '<' || c == '>' || c == ':' || c == ';' || c == '.' || c == '/') { return p - 1; } p++; if (p == string.length) { return p - 1; } } } /** * Scans the given string for a class type signature starting at the given index and returns the index of the last character. * <p/> * <pre> * ClassTypeSignature: * { <b>L</b> | <b>Q</b> } Identifier * { { <b>/</b> | <b>.</b> Identifier [ <b><</b> TypeArgumentSignature* <b>></b> ] } * <b>;</b> * </pre> * <p/> * Note that although all "/"-identifiers most come before "."-identifiers, there is no syntactic ambiguity. This method will * accept them without complaint. * * @param string * the signature string * @param start * the 0-based character index of the first character * @return the 0-based character index of the last character * @throws IllegalArgumentException * if this is not a class type signature */ public static int scanClassTypeSignature(char[] string, int start) { // need a minimum 3 chars "Lx;" if (start >= string.length - 2) { throw new IllegalArgumentException(); } // must start in "L" or "Q" char c = string[start]; if (c != C_RESOLVED && c != C_UNRESOLVED) { return -1; } int p = start + 1; while (true) { if (p >= string.length) { throw new IllegalArgumentException(); } c = string[p]; if (c == C_SEMICOLON) { // all done return p; } else if (c == C_GENERIC_START) { int e = scanTypeArgumentSignatures(string, p); p = e; } else if (c == C_DOT || c == '/') { int id = scanIdentifier(string, p + 1); p = id; } p++; } } /** * Scans the given string for a type bound signature starting at the given index and returns the index of the last character. * <p/> * <pre> * TypeBoundSignature: * <b>[-+]</b> TypeSignature <b>;</b> * <b>*</b></b> * </pre> * * @param string * the signature string * @param start * the 0-based character index of the first character * @return the 0-based character index of the last character * @throws IllegalArgumentException * if this is not a type variable signature */ public static int scanTypeBoundSignature(char[] string, int start) { // need a minimum 1 char for wildcard if (start >= string.length) { throw new IllegalArgumentException(); } char c = string[start]; switch (c) { case C_STAR: return start; case C_SUPER: case C_EXTENDS: // need a minimum 3 chars "+[I" if (start >= string.length - 2) { throw new IllegalArgumentException(); } break; default: // must start in "+/-" throw new IllegalArgumentException(); } c = string[++start]; switch (c) { case C_CAPTURE: return scanCaptureTypeSignature(string, start); case C_SUPER: case C_EXTENDS: return scanTypeBoundSignature(string, start); case C_RESOLVED: case C_UNRESOLVED: return scanClassTypeSignature(string, start); case C_TYPE_VARIABLE: return scanTypeVariableSignature(string, start); case C_ARRAY: return scanArrayTypeSignature(string, start); case C_STAR: return start; default: throw new IllegalArgumentException(); } } /** * Scans the given string for a list of type argument signatures starting at the given index and returns the index of the last * character. * <p/> * <pre> * TypeArgumentSignatures: * <b><</b> TypeArgumentSignature* <b>></b> * </pre> * <p/> * Note that although there is supposed to be at least one type argument, there is no syntactic ambiguity if there are none. * This method will accept zero type argument signatures without complaint. * * @param string * the signature string * @param start * the 0-based character index of the first character * @return the 0-based character index of the last character * @throws IllegalArgumentException * if this is not a list of type arguments signatures */ public static int scanTypeArgumentSignatures(char[] string, int start) { // need a minimum 2 char "<>" if (start >= string.length - 1) { throw new IllegalArgumentException(); } char c = string[start]; if (c != C_GENERIC_START) { throw new IllegalArgumentException(); } int p = start + 1; while (true) { if (p >= string.length) { throw new IllegalArgumentException(); } c = string[p]; if (c == C_GENERIC_END) { return p; } int e = scanTypeArgumentSignature(string, p); p = e + 1; } } /** * Scans the given string for a type argument signature starting at the given index and returns the index of the last * character. * <p/> * <pre> * TypeArgumentSignature: * <b>*</b> * | <b>+</b> TypeSignature * | <b>-</b> TypeSignature * | TypeSignature * </pre> * <p/> * Note that although base types are not allowed in type arguments, there is no syntactic ambiguity. This method will accept * them without complaint. * * @param string * the signature string * @param start * the 0-based character index of the first character * @return the 0-based character index of the last character * @throws IllegalArgumentException * if this is not a type argument signature */ public static int scanTypeArgumentSignature(char[] string, int start) { // need a minimum 1 char if (start >= string.length) { throw new IllegalArgumentException(); } char c = string[start]; switch (c) { case C_STAR: return start; case C_EXTENDS: case C_SUPER: return scanTypeBoundSignature(string, start); default: return scanTypeSignature(string, start); } } }