Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*******************************************************************************
* Copyright (c) 2000, 2023 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* [email protected] - Contribution for bug 3292227
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.util;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ClassFile;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.batch.FileSystem;
import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath;
import org.eclipse.jdt.internal.compiler.batch.Main;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
public class Util implements SuffixConstants {
/**
* Character constant indicating the primitive type boolean in a signature.
* Value is 'Z'.
*/
public static final char C_BOOLEAN = 'Z';
/**
* Character constant indicating the primitive type byte in a signature.
* Value is 'B'.
*/
public static final char C_BYTE = 'B';
/**
* Character constant indicating the primitive type char in a signature.
* Value is 'C'.
*/
public static final char C_CHAR = 'C';
/**
* Character constant indicating the primitive type double in a signature.
* Value is 'D'.
*/
public static final char C_DOUBLE = 'D';
/**
* Character constant indicating the primitive type float in a signature.
* Value is 'F'.
*/
public static final char C_FLOAT = 'F';
/**
* Character constant indicating the primitive type int in a signature.
* Value is 'I'.
*/
public static final char C_INT = 'I';
/**
* Character constant indicating the semicolon in a signature.
* Value is ';'.
*/
public static final char C_SEMICOLON = ';';
/**
* Character constant indicating the colon in a signature.
* Value is ':'.
* @since 3.0
*/
public static final char C_COLON = ':';
/**
* Character constant indicating the primitive type long in a signature.
* Value is 'J'.
*/
public static final char C_LONG = 'J';
/**
* Character constant indicating the primitive type short in a signature.
* Value is 'S'.
*/
public static final char C_SHORT = 'S';
/**
* Character constant indicating result type void in a signature.
* Value is 'V'.
*/
public static final char C_VOID = 'V';
/**
* Character constant indicating the start of a resolved type variable in a
* signature. Value is 'T'.
* @since 3.0
*/
public static final char C_TYPE_VARIABLE = 'T';
/**
* Character constant indicating an unbound wildcard type argument
* in a signature.
* Value is '*'.
* @since 3.0
*/
public static final char C_STAR = '*';
/**
* Character constant indicating an exception in a signature.
* Value is '^'.
* @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 '+'.
* @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 '-'.
* @since 3.1
*/
public static final char C_SUPER = '-';
/**
* Character constant indicating the dot in a signature.
* Value is '.'.
*/
public static final char C_DOT = '.';
/**
* Character constant indicating the dollar in a signature.
* Value is '$'.
*/
public static final char C_DOLLAR = '$';
/**
* Character constant indicating an array type in a signature.
* Value is '['.
*/
public static final char C_ARRAY = '[';
/**
* Character constant indicating the start of a resolved, named type in a
* signature. Value is 'L'.
*/
public static final char C_RESOLVED = 'L';
/**
* Character constant indicating the start of an unresolved, named type in a
* signature. Value is 'Q'.
*/
public static final char C_UNRESOLVED = 'Q';
/**
* Character constant indicating the end of a named type in a signature.
* Value is ';'.
*/
public static final char C_NAME_END = ';';
/**
* Character constant indicating the start of a parameter type list in a
* signature. Value is '('.
*/
public static final char C_PARAM_START = '(';
/**
* Character constant indicating the end of a parameter type list in a
* signature. Value is ')'.
*/
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 '<'.
* @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 '>'.
* @since 3.0
*/
public static final char C_GENERIC_END = '>';
/**
* Character constant indicating a capture of a wildcard type in a
* signature. Value is '!'.
* @since 3.1
*/
public static final char C_CAPTURE = '!';
public interface Displayable {
String displayString(Object o);
}
private static final int DEFAULT_WRITING_SIZE = 1024;
public final static String UTF_8 = "UTF-8"; //$NON-NLS-1$
public static final String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$
public static final String EMPTY_STRING = new String(CharOperation.NO_CHAR);
/**
* @since 3.14
*/
public static final String COMMA_SEPARATOR = new String(CharOperation.COMMA_SEPARATOR);
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()));
}
}
StringBuilder outDir = new StringBuilder(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), encoding);
}
/**
* 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 {
return Files.readAllBytes(file.toPath());
}
/**
* 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 {
try (InputStream stream = new FileInputStream(file)) {
return getInputStreamAsCharArray(stream, encoding);
}
}
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));
}
}
/**
* Returns the given input stream's contents as a byte array.
* All bytes in the stream are returned.
* Note this doesn't close the stream.
* @throws IOException if a problem occurred reading the stream.
*/
public static byte[] getInputStreamAsByteArray(InputStream input) throws IOException {
return input.readAllBytes(); // will have even slighly better performance as of JDK17+ see JDK-8264777
}
/**
* Returns the given input stream's first bytes as array.
* Note this doesn't close the stream.
* @throws IOException if a problem occurred reading the stream.
*/
public static byte[] readNBytes(InputStream input, int byteLength) throws IOException {
return input.readNBytes(byteLength);
}
private static Map bomByEncoding = new HashMap<>();
static {
// org.eclipse.core.runtime.content.IContentDescription.BOM_UTF_8:
bomByEncoding.put("UTF-8", new byte[] { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF }); //$NON-NLS-1$
// XXX UTF-16, UTF-32 may have BOM too
// @see org.eclipse.core.runtime.content.IContentDescription.BOM_UTF_16BE ,..
}
/**
* Returns the given input stream's contents as a character array.
* Note this doesn't close the stream.
* @throws IOException if a problem occured reading the stream.
*/
public static char[] getInputStreamAsCharArray(InputStream stream, String encoding)
throws IOException {
byte[] byteContents = getInputStreamAsByteArray(stream);
return getBytesAsCharArray(byteContents, encoding);
}
public static char[] getBytesAsCharArray(byte[] byteContents, String encoding) {
Charset charset;
try {
charset = Charset.forName(encoding);
} catch (IllegalArgumentException e) {
// encoding is not supported
charset = Charset.defaultCharset();
}
// check for BOM in encoded byte content
// (instead of after decoding to avoid array copy after decoding):
byte[] bom = bomByEncoding.get(charset.name());
int start;
if (bom != null && startsWith(byteContents, bom)) {
start = bom.length; // skip BOM
} else {
start = 0;
}
return decode(byteContents, start, byteContents.length - start, charset);
}
/**
* conversionless inmplementation of
*
* @return new String(srcBytes, start, length, charset).toCharArray();
**/
private static char[] decode(byte[] srcBytes, int start, int length, Charset charset) {
ByteBuffer srcBuffer = ByteBuffer.wrap(srcBytes, start, length);
CharBuffer destBuffer = charset.decode(srcBuffer);
char[] dst = destBuffer.array();
int chars = destBuffer.remaining();
if (chars != dst.length) {
dst = Arrays.copyOf(dst, chars);
}
return dst;
}
private static boolean startsWith(byte[] a, byte[] start) {
if (a.length < start.length) {
return false;
}
for (int i = 0; i < start.length; i++) {
if (a[i] != start[i])
return false;
}
return true;
}
/**
* Returns a one line summary for an exception (extracted from its stacktrace: name + first frame)
* @return one line summary for an exception
*/
public static String getExceptionSummary(Throwable exception) {
CharSequence buffer = getStackTrace(exception);
StringBuilder exceptionBuffer = new StringBuilder(50);
exceptionBuffer.append(exception.toString());
// 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.subSequence(line2Start, i));
break lookupLine2;
}
lineSep++;
break;
case ' ' :
case '\t' :
break;
default :
if (lineSep > 0) {
line2Start = i;
lineSep = 0;
}
break;
}
}
return exceptionBuffer.toString();
}
public static CharSequence getStackTrace(Throwable exception) {
StringWriter out = new StringWriter();
PrintWriter s = new PrintWriter(out);
exception.printStackTrace(s);
return out.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 occurred reading the zip entry.
*/
public static byte[] getZipEntryByteContent(ZipEntry ze, ZipFile zip) throws IOException {
try (InputStream inputStream = zip.getInputStream(ze)) {
if (inputStream == null)
throw new IOException("Invalid zip entry name : " + ze.getName()); //$NON-NLS-1$
return inputStream.readAllBytes();
}
}
public static int hashCode(Object[] array) {
int prime = 31;
if (array == null) {
return 0;
}
int result = 1;
for (Object o : array) {
result = prime * result + (o == null ? 0 : o.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
}
public static final int ZIP_FILE = 0;
public static final int JMOD_FILE = 1;
/**
* Returns the kind of archive this file is. The format is one of
* #ZIP_FILE or {@link #JMOD_FILE}
*/
public final static int archiveFormat(String name) {
int lastDot = name.lastIndexOf('.');
if (lastDot == -1)
return -1; // no file extension, it cannot be a zip archive name
if (name.lastIndexOf(File.separatorChar) > lastDot)
return -1; // 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 -1; // 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 ZIP_FILE; // not a ".class" file, so this is a potential archive name
}
}
return -1; // it is a ".class" file, it cannot be a zip archive name
}
if (extensionLength == EXTENSION_jmod.length()) {
for (int i = extensionLength-1; i >=0; i--) {
if (Character.toLowerCase(name.charAt(length - extensionLength + i)) != EXTENSION_jmod.charAt(i)) {
return ZIP_FILE; // not a ".jmod" file, so this is a potential archive name
}
}
return JMOD_FILE;
}
return ZIP_FILE; // 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 (char[] exclusionPattern : exclusionPatterns) {
if (CharOperation.pathMatch(exclusionPattern, 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;
}
/**
* @return true if name.endsWith("jrt-fs.jar")
*/
public final static boolean isJrt(String name) {
return name != null && name.endsWith(JRTUtil.JRT_FS_JAR);
}
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(){
@Override
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$
StringBuilder buffer = new StringBuilder(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;
}
*/
try (BufferedOutputStream output = new BufferedOutputStream(file, DEFAULT_WRITING_SIZE)) {
// 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;
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
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 (TypeBinding argument : arguments) {
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 (ReferenceBinding superInterface : superInterfaces) {
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 (TypeBinding otherUpperBound : upperBounds) {
if ((otherUpperBound.tagBits & TagBits.ContainsNestedTypeReferences) != 0) {
recordNestedType(classFile, otherUpperBound);
}
}
}
} else if (typeBinding.isNestedType()) {
TypeBinding enclosingType = typeBinding;
do {
if (!enclosingType.canBeSeenBy(classFile.referenceBinding.scope))
break;
enclosingType = enclosingType.enclosingType();
} while (enclosingType != null);
boolean onBottomForBug445231 = enclosingType != null;
classFile.recordInnerClasses(typeBinding, onBottomForBug445231);
}
}
/*
* 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 collectVMBootclasspath(List bootclasspaths, File javaHome) {
List classpaths = collectPlatformLibraries(javaHome);
bootclasspaths.addAll(classpaths);
}
public static void collectRunningVMBootclasspath(List bootclasspaths) {
collectVMBootclasspath(bootclasspaths, null);
}
public static long getJDKLevel(File javaHome) {
String version = System.getProperty("java.version"); //$NON-NLS-1$
return CompilerOptions.versionToJdkLevel(version);
}
public static List collectFilesNames() {
return collectPlatformLibraries(null);
}
public static List collectPlatformLibraries(File javaHome) {
/* no bootclasspath specified
* we can try to retrieve the default librairies of the VM used to run
* the batch compiler
*/
String javaversion = null;
javaversion = System.getProperty("java.version"); //$NON-NLS-1$
// Surely, this ain't required anymore?
if (javaversion != null && javaversion.equalsIgnoreCase("1.1.8")) { //$NON-NLS-1$
throw new IllegalStateException();
}
long jdkLevel = CompilerOptions.versionToJdkLevel(javaversion);
if (jdkLevel >= ClassFileConstants.JDK9) {
List filePaths = new ArrayList<>();
if (javaHome == null) {
javaHome = getJavaHome();
}
if (javaHome != null) {
filePaths.add(FileSystem.getJrtClasspath(javaHome.getAbsolutePath(), null, null, null));
return filePaths;
}
}
/*
* 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$
}
}
Set filePaths = new HashSet<>();
if ((bootclasspathProperty != null) && (bootclasspathProperty.length() != 0)) {
StringTokenizer tokenizer = new StringTokenizer(bootclasspathProperty, File.pathSeparator);
while (tokenizer.hasMoreTokens()) {
filePaths.add(tokenizer.nextToken());
}
} else {
// try to get all jars inside the lib folder of the java home
if (javaHome == null) {
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 (File[] current : systemLibrariesJars) {
if (current != null) {
for (File file : current) {
filePaths.add(file.getAbsolutePath());
}
}
}
}
}
}
List classpaths = new ArrayList<>();
for (String filePath : filePaths) {
FileSystem.Classpath currentClasspath = FileSystem.getClasspath(filePath, null, null, null, null);
if (currentClasspath != null) {
classpaths.add(currentClasspath);
}
}
return classpaths;
}
public static int getParameterCount(char[] methodSignature) {
try {
int count = 0;
int i = CharOperation.indexOf(C_PARAM_START, methodSignature);
if (i < 0) {
throw new IllegalArgumentException(String.valueOf(methodSignature));
} else {
i++;
}
for (;;) {
if (methodSignature[i] == C_PARAM_END) {
return count;
}
int e= Util.scanTypeSignature(methodSignature, i);
if (e < 0) {
throw new IllegalArgumentException(String.valueOf(methodSignature));
} else {
i = e + 1;
}
count++;
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException(String.valueOf(methodSignature), e);
}
}
/**
* Scans the given string for a type signature starting at the given index
* and returns the index of the last character.
*
*
* @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
* @exception 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 newIllegalArgumentException(string, start);
}
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 newIllegalArgumentException(string, start);
}
}
/**
* Scans the given string for a base type signature starting at the given index
* and returns the index of the last character.
*
* BaseTypeSignature:
* B | C | D | F | I
* | J | S | V | Z
*
* 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
* @exception 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 newIllegalArgumentException(string, start);
}
char c = string[start];
if ("BCDFIJSVZ".indexOf(c) >= 0) { //$NON-NLS-1$
return start;
} else {
throw newIllegalArgumentException(string, start);
}
}
/**
* Scans the given string for an array type signature starting at the given
* index and returns the index of the last character.
*
* ArrayTypeSignature:
* [ TypeSignature
*
*
* @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
* @exception 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 newIllegalArgumentException(string, start);
}
char c = string[start];
if (c != C_ARRAY) {
throw newIllegalArgumentException(string, start);
}
c = string[++start];
while(c == C_ARRAY) {
// need a minimum 2 char
if (start >= length - 1) {
throw newIllegalArgumentException(string, start);
}
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.
*
* CaptureTypeSignature:
* ! TypeBoundSignature
*
*
* @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
* @exception 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 newIllegalArgumentException(string, start);
}
char c = string[start];
if (c != C_CAPTURE) {
throw newIllegalArgumentException(string, start);
}
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.
*
* TypeVariableSignature:
* T Identifier ;
*
*
* @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
* @exception 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 newIllegalArgumentException(string, start);
}
// must start in "T"
char c = string[start];
if (c != C_TYPE_VARIABLE) {
throw newIllegalArgumentException(string, start);
}
int id = scanIdentifier(string, start + 1);
c = string[id + 1];
if (c == C_SEMICOLON) {
return id + 1;
} else {
throw newIllegalArgumentException(string, start);
}
}
/**
* 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
* @exception 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 newIllegalArgumentException(string, start);
}
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.
*
* 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
* @exception 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 newIllegalArgumentException(string, start);
}
// 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 newIllegalArgumentException(string, start);
}
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.
*
*
* @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
* @exception 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 newIllegalArgumentException(string, start);
}
char c = string[start];
switch (c) {
case C_STAR :
return start;
case C_SUPER :
case C_EXTENDS :
break;
default :
// must start in "+/-"
throw newIllegalArgumentException(string, start);
}
c = string[++start];
if (c != C_STAR && start >= string.length -1) { // unless "-*" we need at least one more char, e.g. after "+[", other variants are even longer
throw new IllegalArgumentException();
}
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 newIllegalArgumentException(string, start);
}
}
/**
* Scans the given string for a list of type argument signatures starting at
* the given index and returns the index of the last character.
*
* 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
* @exception 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 newIllegalArgumentException(string, start);
}
char c = string[start];
if (c != C_GENERIC_START) {
throw newIllegalArgumentException(string, start);
}
int p = start + 1;
while (true) {
if (p >= string.length) {
throw newIllegalArgumentException(string, start);
}
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.
*
* 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
* @exception 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 newIllegalArgumentException(string, start);
}
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);
}
}
public static boolean effectivelyEqual(Object [] one, Object [] two) {
if (one == two)
return true;
int oneLength = one == null ? 0 : one.length;
int twoLength = two == null ? 0 : two.length;
if (oneLength != twoLength)
return false;
if (oneLength == 0)
return true;
for (int i = 0; i < one.length; i++) {
if (one[i] != two[i])
return false;
}
return true;
}
public static void appendEscapedChar(StringBuilder buffer, char c, boolean stringLiteral) {
switch (c) {
case '\b' :
buffer.append("\\b"); //$NON-NLS-1$
break;
case '\t' :
buffer.append("\\t"); //$NON-NLS-1$
break;
case '\n' :
buffer.append("\\n"); //$NON-NLS-1$
break;
case '\f' :
buffer.append("\\f"); //$NON-NLS-1$
break;
case '\r' :
buffer.append("\\r"); //$NON-NLS-1$
break;
case '\"':
if (stringLiteral) {
buffer.append("\\\""); //$NON-NLS-1$
} else {
buffer.append(c);
}
break;
case '\'':
if (stringLiteral) {
buffer.append(c);
} else {
buffer.append("\\\'"); //$NON-NLS-1$
}
break;
case '\\':
buffer.append("\\\\"); //$NON-NLS-1$
break;
default:
if (c >= 0x20) {
buffer.append(c);
} else if (c >= 0x10) {
buffer.append("\\u00").append(Integer.toHexString(c)); //$NON-NLS-1$
} else if (c >= 0) {
buffer.append("\\u000").append(Integer.toHexString(c)); //$NON-NLS-1$
} else {
buffer.append(c);
}
}
}
private static IllegalArgumentException newIllegalArgumentException(char[] string, int start) {
return new IllegalArgumentException("\"" + String.valueOf(string) + "\" at " + start); //$NON-NLS-1$ //$NON-NLS-2$
}
}