org.eclipse.jdt.internal.compiler.util.Util Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
Eclipse Compiler for Java(TM)
/*******************************************************************************
* 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.BufferedInputStream;
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.net.URI;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
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.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
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 {
InputStream stream = null;
try {
stream = new BufferedInputStream(new FileInputStream(file));
return getInputStreamAsByteArray(stream);
} 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, 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));
}
}
/**
* 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);
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)
* @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 = stringWriter.getBuffer();
StringBuffer exceptionBuffer = new StringBuffer(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.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 occurred 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 readNBytes(stream, (int) ze.getSize());
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// ignore
}
}
}
}
/**
* Creates and returns a ClassFileReader.
* @throws IOException if a problem occurred reading the zip entry.
*/
public static ClassFileReader getZipEntryClassFile(String zipName, String fileName) throws IOException, ClassFormatException {
try(ZipFile zip = new ZipFile(zipName)) {
ZipEntry entry = zip.getEntry(fileName);
if (entry == null) {
throw new IOException("Invalid zip entry name : " + fileName + " in " + zipName); //$NON-NLS-1$ //$NON-NLS-2$
}
try(BufferedInputStream stream = new BufferedInputStream(zip.getInputStream(entry))){
URI uri = toJarUri(zipName, fileName);
ClassFileReader reader = new ClassFileReader(uri, getInputStreamAsByteArray(stream), fileName.toCharArray());
return reader;
}
}
}
private static URI toJarUri(String zipName, String fileName) {
Path zipfile = Paths.get(zipName);
URI uri = URI.create("jar:file://" + zipfile.toUri().getRawPath() + "!/" + fileName); //$NON-NLS-1$ //$NON-NLS-2$
return uri;
}
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
}
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 (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;
}
/**
* Returns true iff str.toLowerCase().endsWith("jrt-fs.jar")
* implementation is not creating extra strings.
*/
public final static boolean isJrt(String name) {
return 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 (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()) {
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 (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++) {
filePaths.add(current[j].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.
*
* TypeSignature:
* | BaseTypeSignature
* | ArrayTypeSignature
* | ClassTypeSignature
* | TypeVariableSignature
*
*
* @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.
*
* ClassTypeSignature:
* { L | Q } Identifier
* { { / | . Identifier [ < TypeArgumentSignature* > ] }
* ;
*
* 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.
*
* TypeBoundSignature:
* [-+] 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 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.
*
* TypeArgumentSignatures:
* < TypeArgumentSignature* >
*
* 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.
*
* TypeArgumentSignature:
* *
* | + TypeSignature
* | - TypeSignature
* | TypeSignature
*
* 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(StringBuffer 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$
}
}