/*******************************************************************************
* 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);
}
}
}