/*******************************************************************************
* Copyright (c) 2004, 2012 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.che.jdt.internal.core.util;
import org.eclipse.che.jdt.dom.JavaConventions;
import org.eclipse.che.jdt.internal.core.Annotation;
import org.eclipse.che.jdt.internal.core.CompilationUnit;
import org.eclipse.che.jdt.internal.core.JavaElement;
import org.eclipse.che.jdt.internal.core.JavaModelManager;
import org.eclipse.che.jdt.internal.core.MemberValuePair;
import org.eclipse.che.jdt.internal.core.PackageFragment;
import org.eclipse.che.jdt.internal.core.PackageFragmentRoot;
import org.eclipse.che.ide.runtime.Assert;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModelStatusConstants;
import org.eclipse.jdt.core.IMemberValuePair;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.UnionTypeReference;
import org.eclipse.jdt.internal.compiler.env.ClassSignature;
import org.eclipse.jdt.internal.compiler.env.EnumConstantSignature;
import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jdt.internal.core.util.KeyToSignature;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
/** Provides convenient utility methods to other types in this package. */
public class Util {
private static final char ARGUMENTS_DELIMITER = '#';
private static final String EMPTY_ARGUMENT = " "; //$NON-NLS-1$
private static final char[] BOOLEAN = "boolean".toCharArray(); //$NON-NLS-1$
private static final char[] BYTE = "byte".toCharArray(); //$NON-NLS-1$
private static final char[] CHAR = "char".toCharArray(); //$NON-NLS-1$
private static final char[] DOUBLE = "double".toCharArray(); //$NON-NLS-1$
private static final char[] FLOAT = "float".toCharArray(); //$NON-NLS-1$
private static final char[] INT = "int".toCharArray(); //$NON-NLS-1$
private static final char[] LONG = "long".toCharArray(); //$NON-NLS-1$
private static final char[] SHORT = "short".toCharArray(); //$NON-NLS-1$
private static final char[] VOID = "void".toCharArray(); //$NON-NLS-1$
private static char[][] JAVA_LIKE_EXTENSIONS;
private Util() {
// cannot be instantiated
}
/**
* Returns a new array adding the second array at the end of first array. It answers null if the first and second are null. If
* the first array is null or if it is empty, then a new array is created with second. If the second array is null, then the
* first array is returned. <br>
* <br>
* For example:
* <ol>
* <li>
* <p/>
* <pre>
* first = null
* second = "a"
* => result = {"a"}
* </pre>
* <p/>
* <li>
* <p/>
* <pre>
* first = {"a"}
* second = null
* => result = {"a"}
* </pre>
* <p/>
* </li>
* <li>
* <p/>
* <pre>
* first = {"a"}
* second = {"b"}
* => result = {"a", "b"}
* </pre>
* <p/>
* </li>
* </ol>
*
* @param first
* the first array to concatenate
* @param second
* the array to add at the end of the first array
* @return a new array adding the second array at the end of first array, or null if the two arrays are null.
*/
public static final String[] arrayConcat(String[] first, String second) {
if (second == null)
return first;
if (first == null)
return new String[]{second};
int length = first.length;
if (first.length == 0) {
return new String[]{second};
}
String[] result = new String[length + 1];
System.arraycopy(first, 0, result, 0, length);
result[length] = second;
return result;
}
/**
* Checks the type signature in String sig, starting at start and ending before end (end is not included). Returns the index of
* the character immediately after the signature if valid, or -1 if not valid.
*/
private static int checkTypeSignature(String sig, int start, int end, boolean allowVoid) {
if (start >= end)
return -1;
int i = start;
char c = sig.charAt(i++);
int nestingDepth = 0;
while (c == '[') {
++nestingDepth;
if (i >= end)
return -1;
c = sig.charAt(i++);
}
switch (c) {
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
break;
case 'V':
if (!allowVoid)
return -1;
// array of void is not allowed
if (nestingDepth != 0)
return -1;
break;
case 'L':
int semicolon = sig.indexOf(';', i);
// Must have at least one character between L and ;
if (semicolon <= i || semicolon >= end)
return -1;
i = semicolon + 1;
break;
default:
return -1;
}
return i;
}
/**
* Simple replacement for clone() method for GWT
*
* @param array
* @return cloned array
*/
@SuppressWarnings("unchecked")
public static <T> T[] clone(T[] array) {
return (T[])Arrays.asList(array).toArray();
}
/** Combines two hash codes to make a new one. */
public static int combineHashCodes(int hashCode1, int hashCode2) {
return hashCode1 * 17 + hashCode2;
}
/**
* Compares two byte arrays. Returns <0 if a byte in a is less than the corresponding byte in b, or if a is shorter, or if a is
* null. Returns >0 if a byte in a is greater than the corresponding byte in b, or if a is longer, or if b is null. Returns 0
* if they are equal or both null.
*/
public static int compare(byte[] a, byte[] b) {
if (a == b)
return 0;
if (a == null)
return -1;
if (b == null)
return 1;
int len = Math.min(a.length, b.length);
for (int i = 0; i < len; ++i) {
int diff = a[i] - b[i];
if (diff != 0)
return diff;
}
if (a.length > len)
return 1;
if (b.length > len)
return -1;
return 0;
}
/**
* Compares two strings lexicographically. The comparison is based on the Unicode value of each character in the strings.
*
* @return the value <code>0</code> if the str1 is equal to str2; a value less than <code>0</code> if str1 is lexicographically
* less than str2; and a value greater than <code>0</code> if str1 is lexicographically greater than str2.
*/
public static int compare(char[] str1, char[] str2) {
int len1 = str1.length;
int len2 = str2.length;
int n = Math.min(len1, len2);
int i = 0;
while (n-- != 0) {
char c1 = str1[i];
char c2 = str2[i++];
if (c1 != c2) {
return c1 - c2;
}
}
return len1 - len2;
}
/** Concatenate a String[] compound name to a continuous char[]. */
public static char[] concatCompoundNameToCharArray(String[] compoundName) {
if (compoundName == null)
return null;
int length = compoundName.length;
if (length == 0)
return new char[0];
int size = 0;
for (int i = 0; i < length; i++) {
size += compoundName[i].length();
}
char[] compoundChars = new char[size + length - 1];
int pos = 0;
for (int i = 0; i < length; i++) {
String name = compoundName[i];
if (i > 0)
compoundChars[pos++] = '.';
int nameLength = name.length();
name.getChars(0, nameLength, compoundChars, pos);
pos += nameLength;
}
return compoundChars;
}
public static String concatenateName(String name1, String name2, char separator) {
StringBuffer buf = new StringBuffer();
if (name1 != null && name1.length() > 0) {
buf.append(name1);
}
if (name2 != null && name2.length() > 0) {
if (buf.length() > 0) {
buf.append(separator);
}
buf.append(name2);
}
return buf.toString();
}
/**
* Returns the concatenation of the given array parts using the given separator between each part. <br>
* <br>
* For example:<br>
* <ol>
* <li>
* <p/>
* <pre>
* array = {"a", "b"}
* separator = '.'
* => result = "a.b"
* </pre>
* <p/>
* </li>
* <li>
* <p/>
* <pre>
* array = {}
* separator = '.'
* => result = ""
* </pre>
* <p/>
* </li>
* </ol>
*
* @param array
* the given array
* @param separator
* the given separator
* @return the concatenation of the given array parts using the given separator between each part
*/
public static final String concatWith(String[] array, char separator) {
StringBuffer buffer = new StringBuffer();
for (int i = 0, length = array.length; i < length; i++) {
buffer.append(array[i]);
if (i < length - 1)
buffer.append(separator);
}
return buffer.toString();
}
/**
* Returns the concatenation of the given array parts using the given separator between each part and appending the given name
* at the end. <br>
* <br>
* For example:<br>
* <ol>
* <li>
* <p/>
* <pre>
* name = "c"
* array = { "a", "b" }
* separator = '.'
* => result = "a.b.c"
* </pre>
* <p/>
* </li>
* <li>
* <p/>
* <pre>
* name = null
* array = { "a", "b" }
* separator = '.'
* => result = "a.b"
* </pre>
* <p/>
* </li>
* <li>
* <p/>
* <pre>
* name = " c"
* array = null
* separator = '.'
* => result = "c"
* </pre>
* <p/>
* </li>
* </ol>
*
* @param array
* the given array
* @param name
* the given name
* @param separator
* the given separator
* @return the concatenation of the given array parts using the given separator between each part and appending the given name
* at the end
*/
public static final String concatWith(String[] array, String name, char separator) {
if (array == null || array.length == 0)
return name;
if (name == null || name.length() == 0)
return concatWith(array, separator);
StringBuffer buffer = new StringBuffer();
for (int i = 0, length = array.length; i < length; i++) {
buffer.append(array[i]);
buffer.append(separator);
}
buffer.append(name);
return buffer.toString();
}
/** Converts a type signature from the IBinaryType representation to the DC representation. */
public static String convertTypeSignature(char[] sig, int start, int length) {
return new String(sig, start, length).replace('/', '.');
}
/*
* Returns the default java extension (".java"). To be used when the extension is not known.
*/
public static String defaultJavaExtension() {
return SuffixConstants.SUFFIX_STRING_java;
}
/** Returns true iff str.toLowerCase().endsWith(end.toLowerCase()) implementation is not creating extra strings. */
public final static boolean endsWithIgnoreCase(String str, String end) {
int strLength = str == null ? 0 : str.length();
int endLength = end == null ? 0 : end.length();
// return false if the string is smaller than the end.
if (endLength > strLength)
return false;
// return false if any character of the end are
// not the same in lower case.
for (int i = 1; i <= endLength; i++) {
if (ScannerHelper.toLowerCase(end.charAt(endLength - i)) != ScannerHelper.toLowerCase(str
.charAt(strLength - i)))
return false;
}
return true;
}
/**
* Compares two arrays using equals() on the elements. Neither can be null. Only the first len elements are compared. Return
* false if either array is shorter than len.
*/
public static boolean equalArrays(Object[] a, Object[] b, int len) {
if (a == b)
return true;
if (a.length < len || b.length < len)
return false;
for (int i = 0; i < len; ++i) {
if (a[i] == null) {
if (b[i] != null)
return false;
} else {
if (!a[i].equals(b[i]))
return false;
}
}
return true;
}
/**
* Compares two arrays using equals() on the elements. Either or both arrays may be null. Returns true if both are null.
* Returns false if only one is null. If both are arrays, returns true iff they have the same length and all elements are
* equal.
*/
public static boolean equalArraysOrNull(int[] a, int[] b) {
if (a == b)
return true;
if (a == null || b == null)
return false;
int len = a.length;
if (len != b.length)
return false;
for (int i = 0; i < len; ++i) {
if (a[i] != b[i])
return false;
}
return true;
}
/**
* Compares two arrays using equals() on the elements. Either or both arrays may be null. Returns true if both are null.
* Returns false if only one is null. If both are arrays, returns true iff they have the same length and all elements compare
* true with equals.
*/
public static boolean equalArraysOrNull(Object[] a, Object[] b) {
if (a == b)
return true;
if (a == null || b == null)
return false;
int len = a.length;
if (len != b.length)
return false;
// walk array from end to beginning as this optimizes package name cases
// where the first part is always the same (e.g. org.eclipse.jdt)
for (int i = len - 1; i >= 0; i--) {
if (a[i] == null) {
if (b[i] != null)
return false;
} else {
if (!a[i].equals(b[i]))
return false;
}
}
return true;
}
/**
* Compares two String arrays using equals() on the elements. The arrays are first sorted. Either or both arrays may be null.
* Returns true if both are null. Returns false if only one is null. If both are arrays, returns true iff they have the same
* length and iff, after sorting both arrays, all elements compare true with equals. The original arrays are left untouched.
*/
public static boolean equalArraysOrNullSortFirst(String[] a, String[] b) {
if (a == b)
return true;
if (a == null || b == null)
return false;
int len = a.length;
if (len != b.length)
return false;
if (len >= 2) { // only need to sort if more than two items
a = sortCopy(a);
b = sortCopy(b);
}
for (int i = 0; i < len; ++i) {
if (!a[i].equals(b[i]))
return false;
}
return true;
}
/**
* Compares two objects using equals(). Either or both array may be null. Returns true if both are null. Returns false if only
* one is null. Otherwise, return the result of comparing with equals().
*/
public static boolean equalOrNull(Object a, Object b) {
if (a == b) {
return true;
}
if (a == null || b == null) {
return false;
}
return a.equals(b);
}
/*
* Returns whether the given file name equals to the given string ignoring the java like extension of the file name. Returns
* false if it is not a java like file name.
*/
public static boolean equalsIgnoreJavaLikeExtension(String fileName, String string) {
int fileNameLength = fileName.length();
int stringLength = string.length();
if (fileNameLength < stringLength)
return false;
for (int i = 0; i < stringLength; i++) {
if (fileName.charAt(i) != string.charAt(i)) {
return false;
}
}
char[][] javaLikeExtensions = getJavaLikeExtensions();
suffixes:
for (int i = 0, length = javaLikeExtensions.length; i < length; i++) {
char[] suffix = javaLikeExtensions[i];
int extensionStart = stringLength + 1;
if (extensionStart + suffix.length != fileNameLength)
continue;
if (fileName.charAt(stringLength) != '.')
continue;
for (int j = extensionStart; j < fileNameLength; j++) {
if (fileName.charAt(j) != suffix[j - extensionStart])
continue suffixes;
}
return true;
}
return false;
}
/** Given a qualified name, extract the last component. If the input is not qualified, the same string is answered. */
public static String extractLastName(String qualifiedName) {
int i = qualifiedName.lastIndexOf('.');
if (i == -1)
return qualifiedName;
return qualifiedName.substring(i + 1);
}
/** Extracts the parameter types from a method signature. */
public static String[] extractParameterTypes(char[] sig) {
int count = getParameterCount(sig);
String[] result = new String[count];
if (count == 0)
return result;
int i = CharOperation.indexOf('(', sig) + 1;
count = 0;
int len = sig.length;
int start = i;
for (; ; ) {
if (i == len)
break;
char c = sig[i];
if (c == ')')
break;
if (c == '[') {
++i;
} else if (c == 'L') {
i = CharOperation.indexOf(';', sig, i + 1) + 1;
Assert.isTrue(i != 0);
result[count++] = convertTypeSignature(sig, start, i - start);
start = i;
} else {
++i;
result[count++] = convertTypeSignature(sig, start, i - start);
start = i;
}
}
return result;
}
/** Extracts the return type from a method signature. */
public static String extractReturnType(String sig) {
int i = sig.lastIndexOf(')');
Assert.isTrue(i != -1);
return sig.substring(i + 1);
}
/**
* Finds the first line separator used by the given text.
*
* @return </code>"\n"</code> or </code>"\r"</code> or </code>"\r\n"</code>, or <code>null</code> if none found
*/
public static String findLineSeparator(char[] text) {
// find the first line separator
int length = text.length;
if (length > 0) {
char nextChar = text[0];
for (int i = 0; i < length; i++) {
char currentChar = nextChar;
nextChar = i < length - 1 ? text[i + 1] : ' ';
switch (currentChar) {
case '\n':
return "\n"; //$NON-NLS-1$
case '\r':
return nextChar == '\n' ? "\r\n" : "\r"; //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
// not found
return null;
}
/** Returns the registered Java like extensions. */
public static char[][] getJavaLikeExtensions() {
if (JAVA_LIKE_EXTENSIONS == null) {
HashSet<String> fileExtensions = new HashSet<String>();
fileExtensions.add(SuffixConstants.EXTENSION_java);
int length = fileExtensions.size();
// note that file extensions contains "java" as it is defined in JDT Core's plugin.xml
char[][] extensions = new char[length][];
extensions[0] = SuffixConstants.EXTENSION_java.toCharArray(); // ensure that "java" is first
int index = 1;
Iterator<String> iterator = fileExtensions.iterator();
while (iterator.hasNext()) {
String fileExtension = iterator.next();
if (SuffixConstants.EXTENSION_java.equals(fileExtension))
continue;
extensions[index++] = fileExtension.toCharArray();
}
JAVA_LIKE_EXTENSIONS = extensions;
}
return JAVA_LIKE_EXTENSIONS;
}
/**
* Returns the substring of the given file name, ending at the start of a Java like extension. The entire file name is returned
* if it doesn't end with a Java like extension.
*/
public static String getNameWithoutJavaLikeExtension(String fileName) {
int index = indexOfJavaLikeExtension(fileName);
if (index == -1)
return fileName;
return fileName.substring(0, index);
}
/** Returns the number of parameter types in a method signature. */
public static int getParameterCount(char[] sig) {
int i = CharOperation.indexOf('(', sig) + 1;
Assert.isTrue(i != 0);
int count = 0;
int len = sig.length;
for (; ; ) {
if (i == len)
break;
char c = sig[i];
if (c == ')')
break;
if (c == '[') {
++i;
} else if (c == 'L') {
++count;
i = CharOperation.indexOf(';', sig, i + 1) + 1;
Assert.isTrue(i != 0);
} else {
++count;
++i;
}
}
return count;
}
/** Put all the arguments in one String. */
public static String getProblemArgumentsForMarker(String[] arguments) {
StringBuffer args = new StringBuffer(10);
args.append(arguments.length);
args.append(':');
for (int j = 0; j < arguments.length; j++) {
if (j != 0)
args.append(ARGUMENTS_DELIMITER);
if (arguments[j].length() == 0) {
args.append(EMPTY_ARGUMENT);
} else {
encodeArgument(arguments[j], args);
}
}
return args.toString();
}
/**
* Encode the argument by doubling the '#' if present into the argument value.
* <p/>
* <p>
* This stores the encoded argument into the given buffer.
* </p>
*
* @param argument
* the given argument
* @param buffer
* the buffer in which the encoded argument is stored
*/
private static void encodeArgument(String argument, StringBuffer buffer) {
for (int i = 0, max = argument.length(); i < max; i++) {
char charAt = argument.charAt(i);
switch (charAt) {
case ARGUMENTS_DELIMITER:
buffer.append(ARGUMENTS_DELIMITER).append(ARGUMENTS_DELIMITER);
break;
default:
buffer.append(charAt);
}
}
}
/** Separate all the arguments of a String made by getProblemArgumentsForMarker */
public static String[] getProblemArgumentsFromMarker(String argumentsString) {
if (argumentsString == null) {
return null;
}
int index = argumentsString.indexOf(':');
if (index == -1)
return null;
int length = argumentsString.length();
int numberOfArg = 0;
try {
numberOfArg = Integer.parseInt(argumentsString.substring(0, index));
} catch (NumberFormatException e) {
return null;
}
argumentsString = argumentsString.substring(index + 1, length);
return decodeArgumentString(numberOfArg, argumentsString);
}
private static String[] decodeArgumentString(int length, String argumentsString) {
// decode the argumentString knowing that '#' is doubled if part of the argument value
if (length == 0) {
if (argumentsString.length() != 0) {
return null;
}
return CharOperation.NO_STRINGS;
}
String[] result = new String[length];
int count = 0;
StringBuffer buffer = new StringBuffer();
for (int i = 0, max = argumentsString.length(); i < max; i++) {
char current = argumentsString.charAt(i);
switch (current) {
case ARGUMENTS_DELIMITER:
/*
* check the next character. If this is also ARGUMENTS_DELIMITER then only put one into the decoded argument and
* proceed with the next character
*/
if ((i + 1) == max) {
return null;
}
char next = argumentsString.charAt(i + 1);
if (next == ARGUMENTS_DELIMITER) {
buffer.append(ARGUMENTS_DELIMITER);
i++; // proceed with the next character
} else {
// this means the current argument is over
String currentArgumentContents = String.valueOf(buffer);
if (EMPTY_ARGUMENT.equals(currentArgumentContents)) {
currentArgumentContents = org.eclipse.jdt.internal.compiler.util.Util.EMPTY_STRING;
}
result[count++] = currentArgumentContents;
if (count > length) {
// too many elements - ill-formed
return null;
}
buffer.delete(0, buffer.length());
}
break;
default:
buffer.append(current);
}
}
// process last argument
String currentArgumentContents = String.valueOf(buffer);
if (EMPTY_ARGUMENT.equals(currentArgumentContents)) {
currentArgumentContents = org.eclipse.jdt.internal.compiler.util.Util.EMPTY_STRING;
}
result[count++] = currentArgumentContents;
if (count > length) {
// too many elements - ill-formed
return null;
}
buffer.delete(0, buffer.length());
return result;
}
/*
* Returns the declaring type signature of the element represented by the given binding key. Returns the signature of the
* element if it is a type.
* @return the declaring type signature
*/
public static String getDeclaringTypeSignature(String key) {
KeyToSignature keyToSignature = new KeyToSignature(key, KeyToSignature.DECLARING_TYPE);
keyToSignature.parse();
return keyToSignature.signature.toString();
}
/** Returns a trimmed version the simples names returned by Signature. */
public static String[] getTrimmedSimpleNames(String name) {
String[] result = Signature.getSimpleNames(name);
for (int i = 0, length = result.length; i < length; i++) {
result[i] = result[i].trim();
}
return result;
}
//
// /* Returns the signature of the given type. */
// public static String getSignature(Type type) {
// StringBuffer buffer = new StringBuffer();
// getFullyQualifiedName(type, buffer);
// return Signature.createTypeSignature(buffer.toString(), false/*
// * not resolved in source
// */);
// }
/**
* Returns true if the given name ends with one of the known java like extension.
* (implementation is not creating extra strings)
*/
public final static boolean isJavaLikeFileName(String name) {
if (name == null)
return false;
return indexOfJavaLikeExtension(name) != -1;
}
// /*
// * Appends to the given buffer the fully qualified name (as it appears in the source) of the given type
// */
// private static void getFullyQualifiedName(Type type, StringBuffer buffer) {
// switch (type.getNodeType()) {
// case ASTNode.ARRAY_TYPE:
// ArrayType arrayType = (ArrayType)type;
// getFullyQualifiedName(arrayType.getElementType(), buffer);
// for (int i = 0, length = arrayType.getDimensions(); i < length; i++) {
// buffer.append('[');
// buffer.append(']');
// }
// break;
// case ASTNode.PARAMETERIZED_TYPE:
// ParameterizedType parameterizedType = (ParameterizedType)type;
// getFullyQualifiedName(parameterizedType.getType(), buffer);
// buffer.append('<');
// Iterator<Type> iterator = parameterizedType.typeArguments().iterator();
// boolean isFirst = true;
// while (iterator.hasNext()) {
// if (!isFirst)
// buffer.append(',');
// else
// isFirst = false;
// Type typeArgument = iterator.next();
// getFullyQualifiedName(typeArgument, buffer);
// }
// buffer.append('>');
// break;
// case ASTNode.PRIMITIVE_TYPE:
// buffer.append(((PrimitiveType)type).getPrimitiveTypeCode().toString());
// break;
// case ASTNode.QUALIFIED_TYPE:
// buffer.append(((QualifiedType)type).getName().getFullyQualifiedName());
// break;
// case ASTNode.SIMPLE_TYPE:
// buffer.append(((SimpleType)type).getName().getFullyQualifiedName());
// break;
// case ASTNode.WILDCARD_TYPE:
// buffer.append('?');
// WildcardType wildcardType = (WildcardType)type;
// Type bound = wildcardType.getBound();
// if (bound == null)
// return;
// if (wildcardType.isUpperBound()) {
// buffer.append(" extends "); //$NON-NLS-1$
// } else {
// buffer.append(" super "); //$NON-NLS-1$
// }
// getFullyQualifiedName(bound, buffer);
// break;
// }
// }
/**
* Returns the index of the Java like extension of the given file name or -1 if it doesn't end with a known Java like
* extension. Note this is the index of the '.' even if it is not considered part of the extension.
*/
public static int indexOfJavaLikeExtension(String fileName) {
int fileNameLength = fileName.length();
char[][] javaLikeExtensions = getJavaLikeExtensions();
extensions:
for (int i = 0, length = javaLikeExtensions.length; i < length; i++) {
char[] extension = javaLikeExtensions[i];
int extensionLength = extension.length;
int extensionStart = fileNameLength - extensionLength;
int dotIndex = extensionStart - 1;
if (dotIndex < 0)
continue;
if (fileName.charAt(dotIndex) != '.')
continue;
for (int j = 0; j < extensionLength; j++) {
if (fileName.charAt(extensionStart + j) != extension[j])
continue extensions;
}
return dotIndex;
}
return -1;
}
/** Returns true if the given method signature is valid, false if it is not. */
public static boolean isValidMethodSignature(String sig) {
int len = sig.length();
if (len == 0)
return false;
int i = 0;
char c = sig.charAt(i++);
if (c != '(')
return false;
if (i >= len)
return false;
while (sig.charAt(i) != ')') {
// Void is not allowed as a parameter type.
i = checkTypeSignature(sig, i, len, false);
if (i == -1)
return false;
if (i >= len)
return false;
}
++i;
i = checkTypeSignature(sig, i, len, true);
return i == len;
}
/** Returns true if the given type signature is valid, false if it is not. */
public static boolean isValidTypeSignature(String sig, boolean allowVoid) {
int len = sig.length();
return checkTypeSignature(sig, 0, len, allowVoid) == len;
}
/*
* Returns the simple name of a local type from the given binary type name. The last '$' is at lastDollar. The last character
* of the type name is at end-1.
*/
public static String localTypeName(String binaryTypeName, int lastDollar, int end) {
if (lastDollar > 0 && binaryTypeName.charAt(lastDollar - 1) == '$')
// local name starts with a dollar sign
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=103466)
return binaryTypeName;
int nameStart = lastDollar + 1;
while (nameStart < end && Character.isDigit(binaryTypeName.charAt(nameStart)))
nameStart++;
return binaryTypeName.substring(nameStart, end);
}
/* Add a log entry */
public static void log(Throwable e, String message) {
//TODO log error
// Log.error(Util.class, message, e);
}
/** Returns the length of the common prefix between s1 and s2. */
public static int prefixLength(char[] s1, char[] s2) {
int len = 0;
int max = Math.min(s1.length, s2.length);
for (int i = 0; i < max && s1[i] == s2[i]; ++i)
++len;
return len;
}
/** Returns the length of the common prefix between s1 and s2. */
public static int prefixLength(String s1, String s2) {
int len = 0;
int max = Math.min(s1.length(), s2.length());
for (int i = 0; i < max && s1.charAt(i) == s2.charAt(i); ++i)
++len;
return len;
}
private static void quickSort(char[][] list, int left, int right) {
int original_left = left;
int original_right = right;
char[] mid = list[left + (right - left) / 2];
do {
while (compare(list[left], mid) < 0) {
left++;
}
while (compare(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) {
quickSort(list, original_left, right);
}
if (left < original_right) {
quickSort(list, left, original_right);
}
}
/** Sort the comparable objects in the given collection. */
private static void quickSort(Comparable[] sortedCollection, int left, int right) {
int original_left = left;
int original_right = right;
Comparable mid = sortedCollection[left + (right - left) / 2];
do {
while (sortedCollection[left].compareTo(mid) < 0) {
left++;
}
while (mid.compareTo(sortedCollection[right]) < 0) {
right--;
}
if (left <= right) {
Comparable tmp = sortedCollection[left];
sortedCollection[left] = sortedCollection[right];
sortedCollection[right] = tmp;
left++;
right--;
}
}
while (left <= right);
if (original_left < right) {
quickSort(sortedCollection, original_left, right);
}
if (left < original_right) {
quickSort(sortedCollection, left, original_right);
}
}
private static void quickSort(int[] list, int left, int right) {
int original_left = left;
int original_right = right;
int mid = list[left + (right - left) / 2];
do {
while (list[left] < mid) {
left++;
}
while (mid < list[right]) {
right--;
}
if (left <= right) {
int tmp = list[left];
list[left] = list[right];
list[right] = tmp;
left++;
right--;
}
}
while (left <= right);
if (original_left < right) {
quickSort(list, original_left, right);
}
if (left < original_right) {
quickSort(list, left, original_right);
}
}
/** Sort the objects in the given collection using the given comparer. */
private static void quickSort(Object[] sortedCollection, int left, int right, Comparer comparer) {
int original_left = left;
int original_right = right;
Object mid = sortedCollection[left + (right - left) / 2];
do {
while (comparer.compare(sortedCollection[left], mid) < 0) {
left++;
}
while (comparer.compare(mid, sortedCollection[right]) < 0) {
right--;
}
if (left <= right) {
Object tmp = sortedCollection[left];
sortedCollection[left] = sortedCollection[right];
sortedCollection[right] = tmp;
left++;
right--;
}
}
while (left <= right);
if (original_left < right) {
quickSort(sortedCollection, original_left, right, comparer);
}
if (left < original_right) {
quickSort(sortedCollection, left, original_right, comparer);
}
}
/** Sort the strings in the given collection. */
private static void quickSort(String[] sortedCollection, int left, int right) {
int original_left = left;
int original_right = right;
String mid = sortedCollection[left + (right - left) / 2];
do {
while (sortedCollection[left].compareTo(mid) < 0) {
left++;
}
while (mid.compareTo(sortedCollection[right]) < 0) {
right--;
}
if (left <= right) {
String tmp = sortedCollection[left];
sortedCollection[left] = sortedCollection[right];
sortedCollection[right] = tmp;
left++;
right--;
}
}
while (left <= right);
if (original_left < right) {
quickSort(sortedCollection, original_left, right);
}
if (left < original_right) {
quickSort(sortedCollection, left, original_right);
}
}
/* Resets the list of Java-like extensions after a change in content-type. */
public static void resetJavaLikeExtensions() {
JAVA_LIKE_EXTENSIONS = null;
}
/**
* Scans the given string for a type signature starting at the given index and returns the index of the last character.
* <p/>
* <pre>
* TypeSignature:
* | BaseTypeSignature
* | ArrayTypeSignature
* | ClassTypeSignature
* | TypeVariableSignature
* </pre>
*
* @param string
* the signature string
* @param start
* the 0-based character index of the first character
* @return the 0-based character index of the last character
* @throws IllegalArgumentException
* if this is not a type signature
*/
public static int scanTypeSignature(char[] string, int start) {
// this method is used in jdt.debug
return org.eclipse.jdt.internal.compiler.util.Util.scanTypeSignature(string, start);
}
/**
* Return a new array which is the split of the given string using the given divider. The given end is exclusive and the given
* start is inclusive. <br>
* <br>
* For example:
* <ol>
* <li>
* <p/>
* <pre>
* divider = 'b'
* string = "abbaba"
* start = 2
* end = 5
* result => { "", "a", "" }
* </pre>
* <p/>
* </li>
* </ol>
*
* @param divider
* the given divider
* @param string
* the given string
* @param start
* the given starting index
* @param end
* the given ending index
* @return a new array which is the split of the given string using the given divider
* @throws ArrayIndexOutOfBoundsException
* if start is lower than 0 or end is greater than the array length
*/
public static final String[] splitOn(char divider, String string, int start, int end) {
int length = string == null ? 0 : string.length();
if (length == 0 || start > end)
return CharOperation.NO_STRINGS;
int wordCount = 1;
for (int i = start; i < end; i++)
if (string.charAt(i) == divider)
wordCount++;
String[] split = new String[wordCount];
int last = start, currentWord = 0;
for (int i = start; i < end; i++) {
if (string.charAt(i) == divider) {
split[currentWord++] = string.substring(last, i);
last = i + 1;
}
}
split[currentWord] = string.substring(last, end);
return split;
}
public static void sort(char[][] list) {
if (list.length > 1)
quickSort(list, 0, list.length - 1);
}
/** Sorts an array of Comparable objects in place. */
public static void sort(Comparable[] objects) {
if (objects.length > 1)
quickSort(objects, 0, objects.length - 1);
}
public static void sort(int[] list) {
if (list.length > 1)
quickSort(list, 0, list.length - 1);
}
/** Sorts an array of objects in place. The given comparer compares pairs of items. */
public static void sort(Object[] objects, Comparer comparer) {
if (objects.length > 1)
quickSort(objects, 0, objects.length - 1, comparer);
}
/** Sorts an array of strings in place using quicksort. */
public static void sort(String[] strings) {
if (strings.length > 1)
quickSort(strings, 0, strings.length - 1);
}
/** Sorts an array of Comparable objects, returning a new array with the sorted items. The original array is left untouched. */
public static Comparable[] sortCopy(Comparable[] objects) {
int len = objects.length;
Comparable[] copy = new Comparable[len];
System.arraycopy(objects, 0, copy, 0, len);
sort(copy);
return copy;
}
/** Sorts an array of Strings, returning a new array with the sorted items. The original array is left untouched. */
public static Object[] sortCopy(Object[] objects, Comparer comparer) {
int len = objects.length;
Object[] copy = new Object[len];
System.arraycopy(objects, 0, copy, 0, len);
sort(copy, comparer);
return copy;
}
/** Sorts an array of Strings, returning a new array with the sorted items. The original array is left untouched. */
public static String[] sortCopy(String[] objects) {
int len = objects.length;
String[] copy = new String[len];
System.arraycopy(objects, 0, copy, 0, len);
sort(copy);
return copy;
}
/** Converts a char[][] to String, where segments are separated by '.'. */
public static String toString(char[][] c) {
StringBuffer sb = new StringBuffer();
for (int i = 0, max = c.length; i < max; ++i) {
if (i != 0)
sb.append('.');
sb.append(c[i]);
}
return sb.toString();
}
private static void appendArrayTypeSignature(char[] string, int start, StringBuffer buffer, boolean compact) {
int length = string.length;
// need a minimum 2 char
if (start >= length - 1) {
throw new IllegalArgumentException();
}
char c = string[start];
if (c != Signature.C_ARRAY) {
throw new IllegalArgumentException();
}
int index = start;
c = string[++index];
while (c == Signature.C_ARRAY) {
// need a minimum 2 char
if (index >= length - 1) {
throw new IllegalArgumentException();
}
c = string[++index];
}
appendTypeSignature(string, index, buffer, compact);
for (int i = 0, dims = index - start; i < dims; i++) {
buffer.append('[').append(']');
}
}
private static void appendClassTypeSignature(char[] string, int start, StringBuffer buffer, boolean compact) {
char c = string[start];
if (c != Signature.C_RESOLVED) {
return;
}
int p = start + 1;
int checkpoint = buffer.length();
while (true) {
c = string[p];
switch (c) {
case Signature.C_SEMICOLON:
// all done
return;
case Signature.C_DOT:
case '/':
// erase package prefix
if (compact) {
buffer.setLength(checkpoint);
} else {
buffer.append('.');
}
break;
case Signature.C_DOLLAR:
/**
* Convert '$' in resolved type signatures into '.'. NOTE: This assumes that the type signature is an inner type
* signature. This is true in most cases, but someone can define a non-inner type name containing a '$'.
*/
buffer.append('.');
break;
default:
buffer.append(c);
}
p++;
}
}
static void appendTypeSignature(char[] string, int start, StringBuffer buffer, boolean compact) {
char c = string[start];
switch (c) {
case Signature.C_ARRAY:
appendArrayTypeSignature(string, start, buffer, compact);
break;
case Signature.C_RESOLVED:
appendClassTypeSignature(string, start, buffer, compact);
break;
case Signature.C_TYPE_VARIABLE:
int e =
org.eclipse.jdt.internal.compiler.util.Util.scanTypeVariableSignature(string, start);
buffer.append(string, start + 1, e - start - 1);
break;
case Signature.C_BOOLEAN:
buffer.append(BOOLEAN);
break;
case Signature.C_BYTE:
buffer.append(BYTE);
break;
case Signature.C_CHAR:
buffer.append(CHAR);
break;
case Signature.C_DOUBLE:
buffer.append(DOUBLE);
break;
case Signature.C_FLOAT:
buffer.append(FLOAT);
break;
case Signature.C_INT:
buffer.append(INT);
break;
case Signature.C_LONG:
buffer.append(LONG);
break;
case Signature.C_SHORT:
buffer.append(SHORT);
break;
case Signature.C_VOID:
buffer.append(VOID);
break;
}
}
/*
* Returns the unresolved type parameter signatures of the given method e.g. {"QString;", "[int", "[[Qjava.util.Vector;"}
*/
public static String[] typeParameterSignatures(AbstractMethodDeclaration method) {
Argument[] args = method.arguments;
if (args != null) {
int length = args.length;
String[] signatures = new String[length];
for (int i = 0; i < args.length; i++) {
Argument arg = args[i];
signatures[i] = typeSignature(arg.type);
}
return signatures;
}
return CharOperation.NO_STRINGS;
}
/*
* Returns the unresolved type signature of the given type reference, e.g. "QString;", "[int", "[[Qjava.util.Vector;"
*/
public static String typeSignature(TypeReference type) {
String signature = null;
if ((type.bits & ASTNode.IsUnionType) != 0) {
// special treatment for union type reference
UnionTypeReference unionTypeReference = (UnionTypeReference)type;
TypeReference[] typeReferences = unionTypeReference.typeReferences;
int length = typeReferences.length;
String[] typeSignatures = new String[length];
for (int i = 0; i < length; i++) {
char[][] compoundName = typeReferences[i].getParameterizedTypeName();
char[] typeName = CharOperation.concatWith(compoundName, '.');
typeSignatures[i] = Signature.createTypeSignature(typeName, false/*
* don 't resolve
*/);
}
signature = Signature.createIntersectionTypeSignature(typeSignatures);
} else {
char[][] compoundName = type.getParameterizedTypeName();
char[] typeName = CharOperation.concatWith(compoundName, '.');
signature = Signature.createTypeSignature(typeName, false/*
* don't resolve
*/);
}
return signature;
}
/** Asserts that the given method signature is valid. */
public static void validateMethodSignature(String sig) {
Assert.isTrue(isValidMethodSignature(sig));
}
/** Asserts that the given type signature is valid. */
public static void validateTypeSignature(String sig, boolean allowVoid) {
Assert.isTrue(isValidTypeSignature(sig, allowVoid));
}
/**
* Get all type arguments from an array of signatures.
* <p/>
* Example: For following type X<Y<Z>,V<W>,U>.A<B> signatures is: [
* ['L','X','<'
* ,'L','Y','<','L','Z',';'>',';','L','V','<','L','W',';'>',';','L'
* ,'U',';',>',';'], ['L','A','<','L','B',';','>',';'] ]
*
* @param typeSignatures
* Array of signatures (one per each type levels)
* @return char[][][] Array of type arguments for each signature
* @throws IllegalArgumentException
* If one of provided signature is malformed
* @see #splitTypeLevelsSignature(String) Then, this method returns: [ [
* ['L','Y','<','L','Z',';'>',';'], ['L','V','<','L','W',';'>',';'],
* ['L','U',';'] ], [ ['L','B',';'] ] ]
*/
public final static char[][][] getAllTypeArguments(char[][] typeSignatures) {
if (typeSignatures == null)
return null;
int length = typeSignatures.length;
char[][][] typeArguments = new char[length][][];
for (int i = 0; i < length; i++) {
typeArguments[i] = Signature.getTypeArguments(typeSignatures[i]);
}
return typeArguments;
}
/**
* Split signatures of all levels from a type unique key.
* <p/>
* Example: For following type X<Y<Z>,V<W>,U>.A<B>, unique key is: "LX<LY<LZ;>;LV<LW;>;LU;>.LA<LB;>;"
* <p/>
* The return splitted signatures array is: [ ['L','X','<','L','Y','<','L','Z'
* ,';'>',';','L','V','<','L','W',';'>',';','L','U','>',';'], ['L','A','<','L','B',';','>',';']
*
* @param typeSignature
* ParameterizedSourceType type signature
* @return char[][] Array of signatures for each level of given unique key
*/
public final static char[][] splitTypeLevelsSignature(String typeSignature) {
// In case of IJavaElement signature, replace '$' by '.'
char[] source = Signature.removeCapture(typeSignature.toCharArray());
CharOperation.replace(source, '$', '.');
// Init counters and arrays
char[][] signatures = new char[10][];
int signaturesCount = 0;
// int[] lengthes = new int [10];
int paramOpening = 0;
// Scan each signature character
for (int idx = 0, ln = source.length; idx < ln; idx++) {
switch (source[idx]) {
case '>':
paramOpening--;
if (paramOpening == 0) {
if (signaturesCount == signatures.length) {
System.arraycopy(signatures, 0, signatures = new char[signaturesCount + 10][], 0, signaturesCount);
}
}
break;
case '<':
paramOpening++;
break;
case '.':
if (paramOpening == 0) {
if (signaturesCount == signatures.length) {
System.arraycopy(signatures, 0, signatures = new char[signaturesCount + 10][], 0, signaturesCount);
}
signatures[signaturesCount] = new char[idx + 1];
System.arraycopy(source, 0, signatures[signaturesCount], 0, idx);
signatures[signaturesCount][idx] = Signature.C_SEMICOLON;
signaturesCount++;
}
break;
case '/':
source[idx] = '.';
break;
}
}
// Resize signatures array
char[][] typeSignatures = new char[signaturesCount + 1][];
typeSignatures[0] = source;
for (int i = 1, j = signaturesCount - 1; i <= signaturesCount; i++, j--)//NOSONAR
{
typeSignatures[i] = signatures[j];
}
return typeSignatures;
}
public static char[] toAnchor(int startingIndex, char[] methodSignature, char[] methodName, boolean isVargArgs) {
int firstParen = CharOperation.indexOf(Signature.C_PARAM_START, methodSignature);
if (firstParen == -1) {
throw new IllegalArgumentException();
}
StringBuffer buffer = new StringBuffer(methodSignature.length + 10);
// selector
if (methodName != null) {
buffer.append(methodName);
}
// parameters
buffer.append('(');
char[][] pts = Signature.getParameterTypes(methodSignature);
for (int i = startingIndex, max = pts.length; i < max; i++) {
if (i == max - 1) {
appendTypeSignatureForAnchor(pts[i], 0, buffer, isVargArgs);
} else {
appendTypeSignatureForAnchor(pts[i], 0, buffer, false);
}
if (i != pts.length - 1) {
buffer.append(',');
buffer.append(' ');
}
}
buffer.append(')');
char[] result = new char[buffer.length()];
buffer.getChars(0, buffer.length(), result, 0);
return result;
}
private static int appendTypeSignatureForAnchor(char[] string, int start, StringBuffer buffer, boolean isVarArgs) {
// need a minimum 1 char
if (start >= string.length) {
throw new IllegalArgumentException();
}
char c = string[start];
if (isVarArgs) {
switch (c) {
case Signature.C_ARRAY:
return appendArrayTypeSignatureForAnchor(string, start, buffer, true);
case Signature.C_RESOLVED:
case Signature.C_TYPE_VARIABLE:
case Signature.C_BOOLEAN:
case Signature.C_BYTE:
case Signature.C_CHAR:
case Signature.C_DOUBLE:
case Signature.C_FLOAT:
case Signature.C_INT:
case Signature.C_LONG:
case Signature.C_SHORT:
case Signature.C_VOID:
case Signature.C_STAR:
case Signature.C_EXTENDS:
case Signature.C_SUPER:
case Signature.C_CAPTURE:
default:
throw new IllegalArgumentException(); // a var args is an array type
}
} else {
switch (c) {
case Signature.C_ARRAY:
return appendArrayTypeSignatureForAnchor(string, start, buffer, false);
case Signature.C_RESOLVED:
return appendClassTypeSignatureForAnchor(string, start, buffer);
case Signature.C_TYPE_VARIABLE:
int e =
org.eclipse.jdt.internal.compiler.util.Util.scanTypeVariableSignature(string, start);
buffer.append(string, start + 1, e - start - 1);
return e;
case Signature.C_BOOLEAN:
buffer.append(BOOLEAN);
return start;
case Signature.C_BYTE:
buffer.append(BYTE);
return start;
case Signature.C_CHAR:
buffer.append(CHAR);
return start;
case Signature.C_DOUBLE:
buffer.append(DOUBLE);
return start;
case Signature.C_FLOAT:
buffer.append(FLOAT);
return start;
case Signature.C_INT:
buffer.append(INT);
return start;
case Signature.C_LONG:
buffer.append(LONG);
return start;
case Signature.C_SHORT:
buffer.append(SHORT);
return start;
case Signature.C_VOID:
buffer.append(VOID);
return start;
case Signature.C_CAPTURE:
return appendCaptureTypeSignatureForAnchor(string, start, buffer);
case Signature.C_STAR:
case Signature.C_EXTENDS:
case Signature.C_SUPER:
return appendTypeArgumentSignatureForAnchor(string, start, buffer);
default:
throw new IllegalArgumentException();
}
}
}
private static int appendTypeArgumentSignatureForAnchor(char[] string, int start, StringBuffer buffer) {
// need a minimum 1 char
if (start >= string.length) {
throw new IllegalArgumentException();
}
char c = string[start];
switch (c) {
case Signature.C_STAR:
return start;
case Signature.C_EXTENDS:
return appendTypeSignatureForAnchor(string, start + 1, buffer, false);
case Signature.C_SUPER:
return appendTypeSignatureForAnchor(string, start + 1, buffer, false);
default:
return appendTypeSignatureForAnchor(string, start, buffer, false);
}
}
private static int appendCaptureTypeSignatureForAnchor(char[] string, int start, StringBuffer buffer) {
// need a minimum 2 char
if (start >= string.length - 1) {
throw new IllegalArgumentException();
}
char c = string[start];
if (c != Signature.C_CAPTURE) {
throw new IllegalArgumentException();
}
return appendTypeArgumentSignatureForAnchor(string, start + 1, buffer);
}
private static int appendArrayTypeSignatureForAnchor(char[] string, int start, StringBuffer buffer, boolean isVarArgs) {
int length = string.length;
// need a minimum 2 char
if (start >= length - 1) {
throw new IllegalArgumentException();
}
char c = string[start];
if (c != Signature.C_ARRAY) {
throw new IllegalArgumentException();
}
int index = start;
c = string[++index];
while (c == Signature.C_ARRAY) {
// need a minimum 2 char
if (index >= length - 1) {
throw new IllegalArgumentException();
}
c = string[++index];
}
int e = appendTypeSignatureForAnchor(string, index, buffer, false);
for (int i = 1, dims = index - start; i < dims; i++) {
buffer.append('[').append(']');
}
if (isVarArgs) {
buffer.append('.').append('.').append('.');
} else {
buffer.append('[').append(']');
}
return e;
}
private static int appendClassTypeSignatureForAnchor(char[] string, int start, StringBuffer buffer) {
// need a minimum 3 chars "Lx;"
if (start >= string.length - 2) {
throw new IllegalArgumentException();
}
// must start in "L" or "Q"
char c = string[start];
if (c != Signature.C_RESOLVED && c != Signature.C_UNRESOLVED) {
throw new IllegalArgumentException();
}
int p = start + 1;
while (true) {
if (p >= string.length) {
throw new IllegalArgumentException();
}
c = string[p];
switch (c) {
case Signature.C_SEMICOLON:
// all done
return p;
case Signature.C_GENERIC_START:
int e = scanGenericEnd(string, p + 1);
// once we hit type arguments there are no more package prefixes
p = e;
break;
case Signature.C_DOT:
buffer.append('.');
break;
case '/':
buffer.append('/');
break;
case Signature.C_DOLLAR:
// once we hit "$" there are no more package prefixes
/**
* Convert '$' in resolved type signatures into '.'. NOTE: This assumes that the type signature is an inner type
* signature. This is true in most cases, but someone can define a non-inner type name containing a '$'.
*/
buffer.append('.');
break;
default:
buffer.append(c);
}
p++;
}
}
private static int scanGenericEnd(char[] string, int start) {
if (string[start] == Signature.C_GENERIC_END) {
return start;
}
int length = string.length;
int balance = 1;
start++;
while (start <= length) {
switch (string[start]) {
case Signature.C_GENERIC_END:
balance--;
if (balance == 0) {
return start;
}
break;
case Signature.C_GENERIC_START:
balance++;
break;
}
start++;
}
return start;
}
/**
* @return
*/
public static int[] clone(int[] array) {
int result[] = new int[array.length];
System.arraycopy(array, 0, result, 0, array.length);
return result;
}
public static IAnnotation getAnnotation(JavaElement parent, JavaModelManager manager, IBinaryAnnotation binaryAnnotation, String memberValuePairName) {
char[] typeName = org.eclipse.jdt.core.Signature.toCharArray(CharOperation.replaceOnCopy(binaryAnnotation.getTypeName(), '/', '.'));
return new Annotation(parent,manager, new String(typeName), memberValuePairName);
}
public static Object getAnnotationMemberValue(JavaElement parent, JavaModelManager manager, MemberValuePair memberValuePair, Object binaryValue) {
if (binaryValue instanceof Constant) {
return getAnnotationMemberValue(memberValuePair, (Constant) binaryValue);
} else if (binaryValue instanceof IBinaryAnnotation) {
memberValuePair.valueKind = IMemberValuePair.K_ANNOTATION;
return getAnnotation(parent,manager, (IBinaryAnnotation) binaryValue, memberValuePair.getMemberName());
} else if (binaryValue instanceof ClassSignature) {
memberValuePair.valueKind = IMemberValuePair.K_CLASS;
char[] className = Signature.toCharArray(CharOperation.replaceOnCopy(((ClassSignature) binaryValue).getTypeName(), '/', '.'));
return new String(className);
} else if (binaryValue instanceof EnumConstantSignature) {
memberValuePair.valueKind = IMemberValuePair.K_QUALIFIED_NAME;
EnumConstantSignature enumConstant = (EnumConstantSignature) binaryValue;
char[] enumName = Signature.toCharArray(CharOperation.replaceOnCopy(enumConstant.getTypeName(), '/', '.'));
char[] qualifiedName = CharOperation.concat(enumName, enumConstant.getEnumConstantName(), '.');
return new String(qualifiedName);
} else if (binaryValue instanceof Object[]) {
memberValuePair.valueKind = -1; // modified below by the first call to getMemberValue(...)
Object[] binaryValues = (Object[]) binaryValue;
int length = binaryValues.length;
Object[] values = new Object[length];
for (int i = 0; i < length; i++) {
int previousValueKind = memberValuePair.valueKind;
Object value = getAnnotationMemberValue(parent,manager, memberValuePair, binaryValues[i]);
if (previousValueKind != -1 && memberValuePair.valueKind != previousValueKind) {
// values are heterogeneous, value kind is thus unknown
memberValuePair.valueKind = IMemberValuePair.K_UNKNOWN;
}
if (value instanceof Annotation) {
Annotation annotation = (Annotation) value;
for (int j = 0; j < i; j++) {
if (annotation.equals(values[j])) {
annotation.occurrenceCount++;
}
}
}
values[i] = value;
}
if (memberValuePair.valueKind == -1)
memberValuePair.valueKind = IMemberValuePair.K_UNKNOWN;
return values;
} else {
memberValuePair.valueKind = IMemberValuePair.K_UNKNOWN;
return null;
}
}
/*
* Creates a member value from the given constant, and sets the valueKind on the given memberValuePair
*/
public static Object getAnnotationMemberValue(MemberValuePair memberValuePair, Constant constant) {
if (constant == null) {
memberValuePair.valueKind = IMemberValuePair.K_UNKNOWN;
return null;
}
switch (constant.typeID()) {
case TypeIds.T_int :
memberValuePair.valueKind = IMemberValuePair.K_INT;
return new Integer(constant.intValue());
case TypeIds.T_byte :
memberValuePair.valueKind = IMemberValuePair.K_BYTE;
return new Byte(constant.byteValue());
case TypeIds.T_short :
memberValuePair.valueKind = IMemberValuePair.K_SHORT;
return new Short(constant.shortValue());
case TypeIds.T_char :
memberValuePair.valueKind = IMemberValuePair.K_CHAR;
return new Character(constant.charValue());
case TypeIds.T_float :
memberValuePair.valueKind = IMemberValuePair.K_FLOAT;
return new Float(constant.floatValue());
case TypeIds.T_double :
memberValuePair.valueKind = IMemberValuePair.K_DOUBLE;
return new Double(constant.doubleValue());
case TypeIds.T_boolean :
memberValuePair.valueKind = IMemberValuePair.K_BOOLEAN;
return Boolean.valueOf(constant.booleanValue());
case TypeIds.T_long :
memberValuePair.valueKind = IMemberValuePair.K_LONG;
return new Long(constant.longValue());
case TypeIds.T_JavaLangString :
memberValuePair.valueKind = IMemberValuePair.K_STRING;
return constant.stringValue();
default:
memberValuePair.valueKind = IMemberValuePair.K_UNKNOWN;
return null;
}
}
/*
* Creates a member value from the given constant in case of negative numerals,
* and sets the valueKind on the given memberValuePair
*/
public static Object getNegativeAnnotationMemberValue(MemberValuePair memberValuePair, Constant constant) {
if (constant == null) {
memberValuePair.valueKind = IMemberValuePair.K_UNKNOWN;
return null;
}
switch (constant.typeID()) {
case TypeIds.T_int :
memberValuePair.valueKind = IMemberValuePair.K_INT;
return new Integer(constant.intValue() * -1);
case TypeIds.T_float :
memberValuePair.valueKind = IMemberValuePair.K_FLOAT;
return new Float(constant.floatValue() * -1.0f);
case TypeIds.T_double :
memberValuePair.valueKind = IMemberValuePair.K_DOUBLE;
return new Double(constant.doubleValue() * -1.0);
case TypeIds.T_long :
memberValuePair.valueKind = IMemberValuePair.K_LONG;
return new Long(constant.longValue() * -1L);
default:
memberValuePair.valueKind = IMemberValuePair.K_UNKNOWN;
return null;
}
}
/**
* Validate the given compilation unit name.
* A compilation unit name must obey the following rules:
* <ul>
* <li> it must not be null
* <li> it must include the <code>".java"</code> suffix
* <li> its prefix must be a valid identifier
* </ul>
* </p>
* @param name the name of a compilation unit
* @param sourceLevel the source level
* @param complianceLevel the compliance level
* @return a status object with code <code>IStatus.OK</code> if
* the given name is valid as a compilation unit name, otherwise a status
* object indicating what is wrong with the name
*/
public static boolean isValidCompilationUnitName(String name, String sourceLevel, String complianceLevel) {
return JavaConventions.validateCompilationUnitName(name, sourceLevel, complianceLevel).getSeverity() != IStatus.ERROR;
}
/**
* Returns true if the given folder name is valid for a package,
* false if it is not.
* @param folderName the name of the folder
* @param sourceLevel the source level
* @param complianceLevel the compliance level
*/
public static boolean isValidFolderNameForPackage(String folderName, String sourceLevel, String complianceLevel) {
return JavaConventions.validateIdentifier(folderName, sourceLevel, complianceLevel).getSeverity() != IStatus.ERROR;
}
/*
* 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(IPath resourcePath, char[][] inclusionPatterns, char[][] exclusionPatterns, boolean isFolderPath) {
if (inclusionPatterns == null && exclusionPatterns == null) return false;
return org.eclipse.jdt.internal.compiler.util.Util.isExcluded(resourcePath.toString().toCharArray(), inclusionPatterns, exclusionPatterns, isFolderPath);
}
/*
* Returns whether the given java element is exluded from its root's classpath.
* It doesn't check whether the root itself is on the classpath or not
*/
public static final boolean isExcluded(IJavaElement element) {
int elementType = element.getElementType();
switch (elementType) {
case IJavaElement.JAVA_MODEL:
case IJavaElement.JAVA_PROJECT:
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
return false;
case IJavaElement.PACKAGE_FRAGMENT:
PackageFragmentRoot root = (PackageFragmentRoot) element.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
File resource = ((PackageFragment) element).resource();
return resource != null && isExcluded(resource, root.fullInclusionPatternChars(), root.fullExclusionPatternChars());
case IJavaElement.COMPILATION_UNIT:
root = (PackageFragmentRoot) element.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
resource = ((CompilationUnit)element).resource();
if (resource == null)
return false;
if (isExcluded(resource, root.fullInclusionPatternChars(), root.fullExclusionPatternChars()))
return true;
return isExcluded(element.getParent());
default:
IJavaElement cu = element.getAncestor(IJavaElement.COMPILATION_UNIT);
return cu != null && isExcluded(cu);
}
}
/*
* Returns whether the given resource matches one of the exclusion patterns.
* NOTE: should not be asked directly using pkg root pathes
* @see IClasspathEntry#getExclusionPatterns
*/
public final static boolean isExcluded(File resource, char[][] inclusionPatterns, char[][] exclusionPatterns) {
IPath path = new Path(resource.getAbsolutePath());
// ensure that folders are only excluded if all of their children are excluded
int resourceType = resource.isFile()? IResource.FILE : IResource.FOLDER;
return isExcluded(path, inclusionPatterns, exclusionPatterns, resourceType == IResource.FOLDER || resourceType == IResource.PROJECT);
}
/**
* Validate the given .class file name.
* A .class file name must obey the following rules:
* <ul>
* <li> it must not be null
* <li> it must include the <code>".class"</code> suffix
* <li> its prefix must be a valid identifier
* </ul>
* </p>
* @param name the name of a .class file
* @param sourceLevel the source level
* @param complianceLevel the compliance level
* @return a status object with code <code>IStatus.OK</code> if
* the given name is valid as a .class file name, otherwise a status
* object indicating what is wrong with the name
*/
public static boolean isValidClassFileName(String name, String sourceLevel, String complianceLevel) {
return JavaConventions.validateClassFileName(name, sourceLevel, complianceLevel).getSeverity() != IStatus.ERROR;
}
/**
* Converts a String[] to char[][].
*/
public static char[][] toCharArrays(String[] a) {
int len = a.length;
if (len == 0) return CharOperation.NO_CHAR_CHAR;
char[][] result = new char[len][];
for (int i = 0; i < len; ++i) {
result[i] = a[i].toCharArray();
}
return result;
}
/**
* Returns the given file's contents as a character array.
* This Method uses "UTF-8" encoding as default.
*/
public static char[] getResourceContentsAsCharArray(File file) throws JavaModelException {
// Get encoding from file
String encoding;
encoding = "UTF-8";
return getResourceContentsAsCharArray(file, encoding);
}
public static char[] getResourceContentsAsCharArray(File file, String encoding) throws JavaModelException {
// Get file length
// workaround https://bugs.eclipse.org/bugs/show_bug.cgi?id=130736 by using java.io.File if possible
// IPath location = file.getLocation();
long length;
// if (location == null) {
// // non local file
// try {
// URI locationURI = file.getLocationURI();
// if (locationURI == null)
// throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, Messages
// .bind(Messages.file_notFound, file.getFullPath().toString())));
// length = EFS.getStore(locationURI).fetchInfo().getLength();
// } catch (CoreException e) {
// throw new JavaModelException(e, IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST);
// }
// } else {
// // local file
length = file.length();
// }
// Get resource contents
InputStream stream= null;
try {
stream = new FileInputStream(file);
} catch (FileNotFoundException e) {
throw new JavaModelException(e, IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST);
}
try {
return org.eclipse.jdt.internal.compiler.util.Util.getInputStreamAsCharArray(stream, (int) length, encoding);
} catch (IOException e) {
throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
} finally {
try {
stream.close();
} catch (IOException e) {
// ignore
}
}
}
public interface Comparable {
/** Returns 0 if this and c are equal, >0 if this is greater than c, or <0 if this is less than c. */
int compareTo(Comparable c);
}
public interface Comparer {
/** Returns 0 if a and b are equal, >0 if a is greater than b, or <0 if a is less than b. */
int compare(Object a, Object b);
}
}