/******************************************************************************* * Copyright (c) 2000, 2010 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.jdt.internal.core.util; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.net.URI; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.resources.ResourceAttributes; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.preferences.IScopeContext; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IInitializer; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaModelStatusConstants; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMemberValuePair; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaConventions; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.WorkingCopyOwner; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ArrayType; import org.eclipse.jdt.core.dom.ParameterizedType; import org.eclipse.jdt.core.dom.PrimitiveType; import org.eclipse.jdt.core.dom.QualifiedType; import org.eclipse.jdt.core.dom.SimpleType; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.WildcardType; import org.eclipse.jdt.core.util.IClassFileAttribute; import org.eclipse.jdt.core.util.IClassFileReader; import org.eclipse.jdt.core.util.ICodeAttribute; import org.eclipse.jdt.core.util.IFieldInfo; import org.eclipse.jdt.core.util.IMethodInfo; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; 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.env.IDependent; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; 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.Annotation; import org.eclipse.jdt.internal.core.ClassFile; import org.eclipse.jdt.internal.core.JavaElement; import org.eclipse.jdt.internal.core.JavaModelManager; import org.eclipse.jdt.internal.core.Member; import org.eclipse.jdt.internal.core.MemberValuePair; import org.eclipse.jdt.internal.core.PackageFragment; import org.eclipse.jdt.internal.core.PackageFragmentRoot; import org.eclipse.jface.text.BadLocationException; import org.eclipse.text.edits.MalformedTreeException; import org.eclipse.text.edits.TextEdit; /** * Provides convenient utility methods to other types in this package. */ public class Util { 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); } public static interface BindingsToNodesMap { public org.eclipse.jdt.internal.compiler.ast.ASTNode get(Binding binding); } private static final char ARGUMENTS_DELIMITER= '#'; private static final String EMPTY_ARGUMENT= " "; //$NON-NLS-1$ private static char[][] JAVA_LIKE_EXTENSIONS; 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 final char[] INIT= "<init>".toCharArray(); //$NON-NLS-1$ private static final String TASK_PRIORITIES_PROBLEM= "TASK_PRIORITIES_PB"; //$NON-NLS-1$ private static List fgRepeatedMessages= new ArrayList(5); 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> * * <pre> * first = null * second = "a" * => result = {"a"} * </pre> * * <li> * * <pre> * first = {"a"} * second = null * => result = {"a"} * </pre> * * </li> * <li> * * <pre> * first = {"a"} * second = {"b"} * => result = {"a", "b"} * </pre> * * </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; } /** * 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> * * <pre> * array = {"a", "b"} * separator = '.' * => result = "a.b" * </pre> * * </li> * <li> * * <pre> * array = {} * separator = '.' * => result = "" * </pre> * * </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> * * <pre> * name = "c" * array = { "a", "b" } * separator = '.' * => result = "a.b.c" * </pre> * * </li> * <li> * * <pre> * name = null * array = { "a", "b" } * separator = '.' * => result = "a.b" * </pre> * * </li> * <li> * * <pre> * name = " c" * array = null * separator = '.' * => result = "c" * </pre> * * </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; } /** * Apply the given edit on the given string and return the updated string. Return the given * string if anything wrong happen while applying the edit. * * @param original the given string * @param edit the given edit * * @return the updated string */ public final static String editedString(String original, TextEdit edit) { if (edit == null) { return original; } SimpleDocument document= new SimpleDocument(original); try { edit.apply(document, TextEdit.NONE); return document.get(); } catch (MalformedTreeException e) { e.printStackTrace(); } catch (BadLocationException e) { e.printStackTrace(); } return original; } /** * 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 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(Comparable[] a, Comparable[] 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 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); } private static IFile findFirstClassFile(IFolder folder) { try { IResource[] members= folder.members(); for (int i= 0, max= members.length; i < max; i++) { IResource member= members[i]; if (member.getType() == IResource.FOLDER) { return findFirstClassFile((IFolder)member); } else if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(member.getName())) { return (IFile)member; } } } catch (CoreException e) { // ignore } return null; } /** * 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; } public static IClassFileAttribute getAttribute(IClassFileReader classFileReader, char[] attributeName) { IClassFileAttribute[] attributes= classFileReader.getAttributes(); for (int i= 0, max= attributes.length; i < max; i++) { if (CharOperation.equals(attributes[i].getAttributeName(), attributeName)) { return attributes[i]; } } return null; } public static IClassFileAttribute getAttribute(ICodeAttribute codeAttribute, char[] attributeName) { IClassFileAttribute[] attributes= codeAttribute.getAttributes(); for (int i= 0, max= attributes.length; i < max; i++) { if (CharOperation.equals(attributes[i].getAttributeName(), attributeName)) { return attributes[i]; } } return null; } public static IClassFileAttribute getAttribute(IFieldInfo fieldInfo, char[] attributeName) { IClassFileAttribute[] attributes= fieldInfo.getAttributes(); for (int i= 0, max= attributes.length; i < max; i++) { if (CharOperation.equals(attributes[i].getAttributeName(), attributeName)) { return attributes[i]; } } return null; } public static IClassFileAttribute getAttribute(IMethodInfo methodInfo, char[] attributeName) { IClassFileAttribute[] attributes= methodInfo.getAttributes(); for (int i= 0, max= attributes.length; i < max; i++) { if (CharOperation.equals(attributes[i].getAttributeName(), attributeName)) { return attributes[i]; } } return null; } private static IClassFile getClassFile(char[] fileName) { int jarSeparator= CharOperation.indexOf(IDependent.JAR_FILE_ENTRY_SEPARATOR, fileName); int pkgEnd= CharOperation.lastIndexOf('/', fileName); // pkgEnd is exclusive if (pkgEnd == -1) pkgEnd= CharOperation.lastIndexOf(File.separatorChar, fileName); if (jarSeparator != -1 && pkgEnd < jarSeparator) // if in a jar and no slash, it is a default package -> pkgEnd should be equal to jarSeparator pkgEnd= jarSeparator; if (pkgEnd == -1) return null; IPackageFragment pkg= getPackageFragment(fileName, pkgEnd, jarSeparator); if (pkg == null) return null; int start; return pkg.getClassFile(new String(fileName, start= pkgEnd + 1, fileName.length - start)); } private static ICompilationUnit getCompilationUnit(char[] fileName, WorkingCopyOwner workingCopyOwner) { char[] slashSeparatedFileName= CharOperation.replaceOnCopy(fileName, File.separatorChar, '/'); int pkgEnd= CharOperation.lastIndexOf('/', slashSeparatedFileName); // pkgEnd is exclusive if (pkgEnd == -1) return null; IPackageFragment pkg= getPackageFragment(slashSeparatedFileName, pkgEnd, -1/*no jar separator for .java files*/); if (pkg == null) return null; int start; ICompilationUnit cu= pkg.getCompilationUnit(new String(slashSeparatedFileName, start= pkgEnd + 1, slashSeparatedFileName.length - start)); if (workingCopyOwner != null) { ICompilationUnit workingCopy= cu.findWorkingCopy(workingCopyOwner); if (workingCopy != null) return workingCopy; } return cu; } /** * Returns the registered Java like extensions. */ public static char[][] getJavaLikeExtensions() { if (JAVA_LIKE_EXTENSIONS == null) { IContentType javaContentType= Platform.getContentTypeManager().getContentType(JavaCore.JAVA_SOURCE_CONTENT_TYPE); HashSet fileExtensions= new HashSet(); // content types derived from java content type should be included (https://bugs.eclipse.org/bugs/show_bug.cgi?id=121715) IContentType[] contentTypes= Platform.getContentTypeManager().getAllContentTypes(); for (int i= 0, length= contentTypes.length; i < length; i++) { if (contentTypes[i].isKindOf(javaContentType)) { // note that javaContentType.isKindOf(javaContentType) == true String[] fileExtension= contentTypes[i].getFileSpecs(IContentType.FILE_EXTENSION_SPEC); for (int j= 0, length2= fileExtension.length; j < length2; j++) { fileExtensions.add(fileExtension[j]); } } } 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 iterator= fileExtensions.iterator(); while (iterator.hasNext()) { String fileExtension= (String)iterator.next(); if (SuffixConstants.EXTENSION_java.equals(fileExtension)) continue; extensions[index++]= fileExtension.toCharArray(); } JAVA_LIKE_EXTENSIONS= extensions; } return JAVA_LIKE_EXTENSIONS; } /** * Get the jdk level of this root. The value can be: * <ul> * <li>major<<16 + minor : see predefined constants on ClassFileConstants</li> * <li> * <code>0</null> if the root is a source package fragment root or if a Java model exception occured * </li> * </ul> * Returns the jdk level */ public static long getJdkLevel(Object targetLibrary) { try { ClassFileReader reader= null; if (targetLibrary instanceof IFolder) { IFile classFile= findFirstClassFile((IFolder)targetLibrary); // only internal classfolders are allowed if (classFile != null) reader= Util.newClassFileReader(classFile); } else { // root is a jar file or a zip file ZipFile jar= null; try { IPath path= null; if (targetLibrary instanceof IResource) { path= ((IResource)targetLibrary).getFullPath(); } else if (targetLibrary instanceof File) { File f= (File)targetLibrary; if (!f.isDirectory()) { path= new Path(((File)targetLibrary).getPath()); } } if (path != null) { jar= JavaModelManager.getJavaModelManager().getZipFile(path); for (Enumeration e= jar.entries(); e.hasMoreElements();) { ZipEntry member= (ZipEntry)e.nextElement(); String entryName= member.getName(); if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(entryName)) { reader= ClassFileReader.read(jar, entryName); break; } } } } catch (CoreException e) { // ignore } finally { JavaModelManager.getJavaModelManager().closeZipFile(jar); } } if (reader != null) { return reader.getVersion(); } } catch (CoreException e) { // ignore } catch (ClassFormatException e) { // ignore } catch (IOException e) { // ignore } return 0; } /** * 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 line separator found in the given text. If it is null, or not found return the * line delimiter for the given project. If the project is null, returns the line separator for * the workspace. If still null, return the system line separator. */ public static String getLineSeparator(String text, IJavaProject project) { String lineSeparator= null; // line delimiter in given text if (text != null && text.length() != 0) { lineSeparator= findLineSeparator(text.toCharArray()); if (lineSeparator != null) return lineSeparator; } if (Platform.isRunning()) { // line delimiter in project preference IScopeContext[] scopeContext; if (project != null) { scopeContext= new IScopeContext[] { new ProjectScope(project.getProject()) }; lineSeparator= Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext); if (lineSeparator != null) return lineSeparator; } // line delimiter in workspace preference scopeContext= new IScopeContext[] { new InstanceScope() }; lineSeparator= Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext); if (lineSeparator != null) return lineSeparator; } // system line delimiter return org.eclipse.jdt.internal.compiler.util.Util.LINE_SEPARATOR; } /** * Returns the line separator used by the given buffer. Uses the given text if none found. * * @return </code>"\n"</code> or </code>"\r"</code> or </code>"\r\n"</code> */ private static String getLineSeparator(char[] text, char[] buffer) { // search in this buffer's contents first String lineSeparator= findLineSeparator(buffer); if (lineSeparator == null) { // search in the given text lineSeparator= findLineSeparator(text); if (lineSeparator == null) { // default to system line separator return getLineSeparator((String)null, (IJavaProject)null); } } return lineSeparator; } private static IPackageFragment getPackageFragment(char[] fileName, int pkgEnd, int jarSeparator) { if (jarSeparator != -1) { String jarMemento= new String(fileName, 0, jarSeparator); PackageFragmentRoot root= (PackageFragmentRoot)JavaCore.create(jarMemento); if (pkgEnd == jarSeparator) return root.getPackageFragment(CharOperation.NO_STRINGS); char[] pkgName= CharOperation.subarray(fileName, jarSeparator + 1, pkgEnd); char[][] compoundName= CharOperation.splitOn('/', pkgName); return root.getPackageFragment(CharOperation.toStrings(compoundName)); } else { Path path= new Path(new String(fileName, 0, pkgEnd)); IWorkspaceRoot workspaceRoot= ResourcesPlugin.getWorkspace().getRoot(); IContainer folder= path.segmentCount() == 1 ? workspaceRoot.getProject(path.lastSegment()) : (IContainer)workspaceRoot.getFolder(path); IJavaElement element= JavaCore.create(folder); if (element == null) return null; switch (element.getElementType()) { case IJavaElement.PACKAGE_FRAGMENT: return (IPackageFragment)element; case IJavaElement.PACKAGE_FRAGMENT_ROOT: return ((PackageFragmentRoot)element).getPackageFragment(CharOperation.NO_STRINGS); case IJavaElement.JAVA_PROJECT: PackageFragmentRoot root= (PackageFragmentRoot)((IJavaProject)element).getPackageFragmentRoot(folder); if (root == null) return null; return root.getPackageFragment(CharOperation.NO_STRINGS); } return null; } } /** * 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> * 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 given file's contents as a byte array. */ public static byte[] getResourceContentsAsByteArray(IFile file) throws JavaModelException { InputStream stream= null; try { stream= file.getContents(true); } catch (CoreException e) { throw new JavaModelException(e); } try { return org.eclipse.jdt.internal.compiler.util.Util.getInputStreamAsByteArray(stream, -1); } catch (IOException e) { throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION); } finally { try { stream.close(); } catch (IOException e) { // ignore } } } /** * Returns the given file's contents as a character array. */ public static char[] getResourceContentsAsCharArray(IFile file) throws JavaModelException { // Get encoding from file String encoding; try { encoding= file.getCharset(); } catch (CoreException ce) { // do not use any encoding encoding= null; } return getResourceContentsAsCharArray(file, encoding); } public static char[] getResourceContentsAsCharArray(IFile 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= location.toFile().length(); } // Get resource contents InputStream stream= null; try { stream= file.getContents(true); } catch (CoreException 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 } } } /* * 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 the source attachment property for this package fragment root's path */ public static String getSourceAttachmentProperty(IPath path) throws JavaModelException { Map rootPathToAttachments= JavaModelManager.getJavaModelManager().rootPathToAttachments; String property= (String)rootPathToAttachments.get(path); if (property == null) { try { property= ResourcesPlugin.getWorkspace().getRoot().getPersistentProperty(getSourceAttachmentPropertyName(path)); if (property == null) { rootPathToAttachments.put(path, PackageFragmentRoot.NO_SOURCE_ATTACHMENT); return null; } rootPathToAttachments.put(path, property); return property; } catch (CoreException e) { throw new JavaModelException(e); } } else if (property.equals(PackageFragmentRoot.NO_SOURCE_ATTACHMENT)) { return null; } else return property; } private static QualifiedName getSourceAttachmentPropertyName(IPath path) { return new QualifiedName(JavaCore.PLUGIN_ID, "sourceattachment: " + path.toOSString()); //$NON-NLS-1$ } public static void setSourceAttachmentProperty(IPath path, String property) { if (property == null) { JavaModelManager.getJavaModelManager().rootPathToAttachments.put(path, PackageFragmentRoot.NO_SOURCE_ATTACHMENT); } else { JavaModelManager.getJavaModelManager().rootPathToAttachments.put(path, property); } try { ResourcesPlugin.getWorkspace().getRoot().setPersistentProperty(getSourceAttachmentPropertyName(path), property); } catch (CoreException e) { e.printStackTrace(); } } /* * 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(); } /* * 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 iterator= parameterizedType.typeArguments().iterator(); boolean isFirst= true; while (iterator.hasNext()) { if (!isFirst) buffer.append(','); else isFirst= false; Type typeArgument= (Type)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 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; } /** * Return the java element corresponding to the given compiler binding. */ public static JavaElement getUnresolvedJavaElement(FieldBinding binding, WorkingCopyOwner workingCopyOwner, BindingsToNodesMap bindingsToNodes) { if (binding.declaringClass == null) return null; // array length JavaElement unresolvedJavaElement= getUnresolvedJavaElement(binding.declaringClass, workingCopyOwner, bindingsToNodes); if (unresolvedJavaElement == null || unresolvedJavaElement.getElementType() != IJavaElement.TYPE) { return null; } return (JavaElement)((IType)unresolvedJavaElement).getField(String.valueOf(binding.name)); } /** * Returns the IInitializer that contains the given local variable in the given type */ public static JavaElement getUnresolvedJavaElement(int localSourceStart, int localSourceEnd, JavaElement type) { try { if (!(type instanceof IType)) return null; IInitializer[] initializers= ((IType)type).getInitializers(); for (int i= 0; i < initializers.length; i++) { IInitializer initializer= initializers[i]; ISourceRange sourceRange= initializer.getSourceRange(); if (sourceRange != null) { int initializerStart= sourceRange.getOffset(); int initializerEnd= initializerStart + sourceRange.getLength(); if (initializerStart <= localSourceStart && localSourceEnd <= initializerEnd) { return (JavaElement)initializer; } } } return null; } catch (JavaModelException e) { return null; } } /** * Return the java element corresponding to the given compiler binding. */ public static JavaElement getUnresolvedJavaElement(MethodBinding methodBinding, WorkingCopyOwner workingCopyOwner, BindingsToNodesMap bindingsToNodes) { JavaElement unresolvedJavaElement= getUnresolvedJavaElement(methodBinding.declaringClass, workingCopyOwner, bindingsToNodes); if (unresolvedJavaElement == null || unresolvedJavaElement.getElementType() != IJavaElement.TYPE) { return null; } IType declaringType= (IType)unresolvedJavaElement; org.eclipse.jdt.internal.compiler.ast.ASTNode node= bindingsToNodes == null ? null : bindingsToNodes.get(methodBinding); if (node != null && !declaringType.isBinary()) { if (node instanceof AnnotationMethodDeclaration) { // node is an AnnotationMethodDeclaration AnnotationMethodDeclaration typeMemberDeclaration= (AnnotationMethodDeclaration)node; return (JavaElement)declaringType.getMethod(String.valueOf(typeMemberDeclaration.selector), CharOperation.NO_STRINGS); // annotation type members don't have parameters } else { // node is an MethodDeclaration MethodDeclaration methodDeclaration= (MethodDeclaration)node; Argument[] arguments= methodDeclaration.arguments; String[] parameterSignatures; if (arguments != null) { parameterSignatures= new String[arguments.length]; for (int i= 0; i < arguments.length; i++) { Argument argument= arguments[i]; TypeReference typeReference= argument.type; int arrayDim= typeReference.dimensions(); String typeSig= Signature.createTypeSignature( CharOperation.concatWith( typeReference.getTypeName(), '.'), false); if (arrayDim > 0) { typeSig= Signature.createArraySignature(typeSig, arrayDim); } parameterSignatures[i]= typeSig; } } else { parameterSignatures= CharOperation.NO_STRINGS; } return (JavaElement)declaringType.getMethod(String.valueOf(methodDeclaration.selector), parameterSignatures); } } else { // case of method not in the created AST, or a binary method org.eclipse.jdt.internal.compiler.lookup.MethodBinding original= methodBinding.original(); String selector= original.isConstructor() ? declaringType.getElementName() : new String(original.selector); boolean isBinary= declaringType.isBinary(); ReferenceBinding enclosingType= original.declaringClass.enclosingType(); boolean isInnerBinaryTypeConstructor= isBinary && original.isConstructor() && enclosingType != null; TypeBinding[] parameters= original.parameters; int length= parameters == null ? 0 : parameters.length; int declaringIndex= isInnerBinaryTypeConstructor ? 1 : 0; String[] parameterSignatures= new String[declaringIndex + length]; if (isInnerBinaryTypeConstructor) parameterSignatures[0]= new String(enclosingType.genericTypeSignature()).replace('/', '.'); for (int i= 0; i < length; i++) { char[] signature= parameters[i].genericTypeSignature(); if (isBinary) { signature= CharOperation.replaceOnCopy(signature, '/', '.'); } else { signature= toUnresolvedTypeSignature(signature); } parameterSignatures[declaringIndex + i]= new String(signature); } IMethod result= declaringType.getMethod(selector, parameterSignatures); if (isBinary) return (JavaElement)result; if (result.exists()) // if perfect match (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=249567 ) return (JavaElement)result; IMethod[] methods= null; try { methods= declaringType.getMethods(); } catch (JavaModelException e) { // declaring type doesn't exist return null; } IMethod[] candidates= Member.findMethods(result, methods); if (candidates == null || candidates.length == 0) return null; return (JavaElement)candidates[0]; } } /** * Return the java element corresponding to the given compiler binding. */ public static JavaElement getUnresolvedJavaElement(TypeBinding typeBinding, WorkingCopyOwner workingCopyOwner, BindingsToNodesMap bindingsToNodes) { if (typeBinding == null) return null; switch (typeBinding.kind()) { case Binding.ARRAY_TYPE: typeBinding= ((org.eclipse.jdt.internal.compiler.lookup.ArrayBinding)typeBinding).leafComponentType(); return getUnresolvedJavaElement(typeBinding, workingCopyOwner, bindingsToNodes); case Binding.BASE_TYPE: case Binding.WILDCARD_TYPE: case Binding.INTERSECTION_TYPE: return null; default: if (typeBinding.isCapture()) return null; } ReferenceBinding referenceBinding; if (typeBinding.isParameterizedType() || typeBinding.isRawType()) referenceBinding= (ReferenceBinding)typeBinding.erasure(); else referenceBinding= (ReferenceBinding)typeBinding; char[] fileName= referenceBinding.getFileName(); if (referenceBinding.isLocalType() || referenceBinding.isAnonymousType()) { // local or anonymous type if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(fileName)) { int jarSeparator= CharOperation.indexOf(IDependent.JAR_FILE_ENTRY_SEPARATOR, fileName); int pkgEnd= CharOperation.lastIndexOf('/', fileName); // pkgEnd is exclusive if (pkgEnd == -1) pkgEnd= CharOperation.lastIndexOf(File.separatorChar, fileName); if (jarSeparator != -1 && pkgEnd < jarSeparator) // if in a jar and no slash, it is a default package -> pkgEnd should be equal to jarSeparator pkgEnd= jarSeparator; if (pkgEnd == -1) return null; IPackageFragment pkg= getPackageFragment(fileName, pkgEnd, jarSeparator); char[] constantPoolName= referenceBinding.constantPoolName(); if (constantPoolName == null) { ClassFile classFile= (ClassFile)getClassFile(fileName); return classFile == null ? null : (JavaElement)classFile.getType(); } pkgEnd= CharOperation.lastIndexOf('/', constantPoolName); char[] classFileName= CharOperation.subarray(constantPoolName, pkgEnd + 1, constantPoolName.length); ClassFile classFile= (ClassFile)pkg.getClassFile(new String(classFileName) + SuffixConstants.SUFFIX_STRING_class); return (JavaElement)classFile.getType(); } ICompilationUnit cu= getCompilationUnit(fileName, workingCopyOwner); if (cu == null) return null; // must use getElementAt(...) as there is no back pointer to the defining method (scope is null after resolution has ended) try { int sourceStart= ((org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding)referenceBinding).sourceStart; return (JavaElement)cu.getElementAt(sourceStart); } catch (JavaModelException e) { // does not exist return null; } } else if (referenceBinding.isTypeVariable()) { // type parameter final String typeVariableName= new String(referenceBinding.sourceName()); org.eclipse.jdt.internal.compiler.lookup.Binding declaringElement= ((org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding)referenceBinding).declaringElement; if (declaringElement instanceof MethodBinding) { IMethod declaringMethod= (IMethod)getUnresolvedJavaElement((MethodBinding)declaringElement, workingCopyOwner, bindingsToNodes); return (JavaElement)declaringMethod.getTypeParameter(typeVariableName); } else { IType declaringType= (IType)getUnresolvedJavaElement((TypeBinding)declaringElement, workingCopyOwner, bindingsToNodes); return (JavaElement)declaringType.getTypeParameter(typeVariableName); } } else { if (fileName == null) return null; // case of a WilCardBinding that doesn't have a corresponding Java element // member or top level type TypeBinding declaringTypeBinding= typeBinding.enclosingType(); if (declaringTypeBinding == null) { // top level type if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(fileName)) { ClassFile classFile= (ClassFile)getClassFile(fileName); if (classFile == null) return null; return (JavaElement)classFile.getType(); } ICompilationUnit cu= getCompilationUnit(fileName, workingCopyOwner); if (cu == null) return null; return (JavaElement)cu.getType(new String(referenceBinding.sourceName())); } else { // member type IType declaringType= (IType)getUnresolvedJavaElement(declaringTypeBinding, workingCopyOwner, bindingsToNodes); if (declaringType == null) return null; return (JavaElement)declaringType.getType(new String(referenceBinding.sourceName())); } } } /* * Returns the index of the most specific argument paths which is strictly enclosing the path to check */ public static int indexOfEnclosingPath(IPath checkedPath, IPath[] paths, int pathCount) { int bestMatch= -1, bestLength= -1; for (int i= 0; i < pathCount; i++) { if (paths[i].equals(checkedPath)) continue; if (paths[i].isPrefixOf(checkedPath)) { int currentLength= paths[i].segmentCount(); if (currentLength > bestLength) { bestLength= currentLength; bestMatch= i; } } } return bestMatch; } /* * 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 the index of the first argument paths which is equal to the path to check */ public static int indexOfMatchingPath(IPath checkedPath, IPath[] paths, int pathCount) { for (int i= 0; i < pathCount; i++) { if (paths[i].equals(checkedPath)) return i; } return -1; } /* * Returns the index of the first argument paths which is strictly nested inside the path to check */ public static int indexOfNestedPath(IPath checkedPath, IPath[] paths, int pathCount) { for (int i= 0; i < pathCount; i++) { if (checkedPath.equals(paths[i])) continue; if (checkedPath.isPrefixOf(paths[i])) return i; } return -1; } /** * Returns whether the local file system supports accessing and modifying the given attribute. */ protected static boolean isAttributeSupported(int attribute) { return (EFS.getLocalFileSystem().attributes() & attribute) != 0; } /** * Returns whether the given resource is read-only or not. * * @param resource * @return <code>true</code> if the resource is read-only, <code>false</code> if it is not or if * the file system does not support the read-only attribute. */ public static boolean isReadOnly(IResource resource) { if (isReadOnlySupported()) { ResourceAttributes resourceAttributes= resource.getResourceAttributes(); if (resourceAttributes == null) return false; // not supported on this platform for this resource return resourceAttributes.isReadOnly(); } return false; } /** * Returns whether the local file system supports accessing and modifying the read only flag. */ public static boolean isReadOnlySupported() { return isAttributeSupported(EFS.ATTRIBUTE_READ_ONLY); } /* * 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); IResource 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= element.getResource(); 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 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 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(IResource resource, char[][] inclusionPatterns, char[][] exclusionPatterns) { IPath path= resource.getFullPath(); // ensure that folders are only excluded if all of their children are excluded int resourceType= resource.getType(); 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; } /** * 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 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) { Throwable nestedException; if (e instanceof JavaModelException && (nestedException= ((JavaModelException)e).getException()) != null) { e= nestedException; } log(new Status( IStatus.ERROR, JavaCore.PLUGIN_ID, IStatus.ERROR, message, e)); } /** * Log a message that is potentially repeated in the same session. The first time this method is * called with a given exception, the exception stack trace is written to the log. * <p> * Only intended for use in debug statements. * </p> * * @param key the given key * @param e the given exception * @throws IllegalArgumentException if the given key is null */ public static void logRepeatedMessage(String key, Exception e) { if (key == null) { throw new IllegalArgumentException("key cannot be null"); //$NON-NLS-1$ } if (fgRepeatedMessages.contains(key)) { return; } fgRepeatedMessages.add(key); log(e); } public static void logRepeatedMessage(String key, int statusErrorID, String message) { if (key == null) { throw new IllegalArgumentException("key cannot be null"); //$NON-NLS-1$ } if (fgRepeatedMessages.contains(key)) { return; } fgRepeatedMessages.add(key); log(statusErrorID, message); } /* * Add a log entry */ public static void log(int statusErrorID, String message) { log(new Status( statusErrorID, JavaCore.PLUGIN_ID, message)); } /* * Add a log entry */ public static void log(IStatus status) { JavaCore.getPlugin().getLog().log(status); } public static void log(Throwable e) { log(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, Messages.code_assist_internal_error, e)); } public static ClassFileReader newClassFileReader(IResource resource) throws CoreException, ClassFormatException, IOException { InputStream in= null; try { in= ((IFile)resource).getContents(true); return ClassFileReader.read(in, resource.getFullPath().toString()); } finally { if (in != null) in.close(); } } /** * Normalizes the cariage returns in the given text. They are all changed to use the given * buffer's line separator. */ public static char[] normalizeCRs(char[] text, char[] buffer) { CharArrayBuffer result= new CharArrayBuffer(); int lineStart= 0; int length= text.length; if (length == 0) return text; String lineSeparator= getLineSeparator(text, buffer); 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': int lineLength= i - lineStart; char[] line= new char[lineLength]; System.arraycopy(text, lineStart, line, 0, lineLength); result.append(line); result.append(lineSeparator); lineStart= i + 1; break; case '\r': lineLength= i - lineStart; if (lineLength >= 0) { line= new char[lineLength]; System.arraycopy(text, lineStart, line, 0, lineLength); result.append(line); result.append(lineSeparator); if (nextChar == '\n') { nextChar= ' '; lineStart= i + 2; } else { // when line separator are mixed in the same file // \r might not be followed by a \n. If not, we should increment // lineStart by one and not by two. lineStart= i + 1; } } else { // when line separator are mixed in the same file // we need to prevent NegativeArraySizeException lineStart= i + 1; } break; } } char[] lastLine; if (lineStart > 0) { int lastLineLength= length - lineStart; if (lastLineLength > 0) { lastLine= new char[lastLineLength]; System.arraycopy(text, lineStart, lastLine, 0, lastLineLength); result.append(lastLine); } return result.getContents(); } return text; } /** * Normalizes the carriage returns in the given text. They are all changed to use given buffer's * line separator. */ public static String normalizeCRs(String text, String buffer) { return new String(normalizeCRs(text.toCharArray(), buffer.toCharArray())); } /** * Converts the given relative path into a package name. Returns null if the path is not a valid * package name. * * @param pkgPath the package path * @param sourceLevel the source level * @param complianceLevel the compliance level */ public static String packageName(IPath pkgPath, String sourceLevel, String complianceLevel) { StringBuffer pkgName= new StringBuffer(IPackageFragment.DEFAULT_PACKAGE_NAME); for (int j= 0, max= pkgPath.segmentCount(); j < max; j++) { String segment= pkgPath.segment(j); if (!isValidFolderNameForPackage(segment, sourceLevel, complianceLevel)) { return null; } pkgName.append(segment); if (j < pkgPath.segmentCount() - 1) { pkgName.append("."); //$NON-NLS-1$ } } return pkgName.toString(); } /** * 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); } } /** * Returns the toString() of the given full path minus the first given number of segments. The * returned string is always a relative path (it has no leading slash) */ public static String relativePath(IPath fullPath, int skipSegmentCount) { boolean hasTrailingSeparator= fullPath.hasTrailingSeparator(); String[] segments= fullPath.segments(); // compute length int length= 0; int max= segments.length; if (max > skipSegmentCount) { for (int i1= skipSegmentCount; i1 < max; i1++) { length+= segments[i1].length(); } //add the separator lengths length+= max - skipSegmentCount - 1; } if (hasTrailingSeparator) length++; char[] result= new char[length]; int offset= 0; int len= segments.length - 1; if (len >= skipSegmentCount) { //append all but the last segment, with separators for (int i= skipSegmentCount; i < len; i++) { int size= segments[i].length(); segments[i].getChars(0, size, result, offset); offset+= size; result[offset++]= '/'; } //append the last segment int size= segments[len].length(); segments[len].getChars(0, size, result, offset); offset+= size; } if (hasTrailingSeparator) result[offset++]= '/'; return new String(result); } /* * Resets the list of Java-like extensions after a change in content-type. */ public static void resetJavaLikeExtensions() { JAVA_LIKE_EXTENSIONS= null; } /** * 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> * * <pre> * divider = 'b' * string = "abbaba" * start = 2 * end = 5 * result => { "", "a", "" } * </pre> * * </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; } /** * Sets or unsets the given resource as read-only in the file system. It's a no-op if the file * system does not support the read-only attribute. * * @param resource The resource to set as read-only * @param readOnly <code>true</code> to set it to read-only, <code>false</code> to unset */ public static void setReadOnly(IResource resource, boolean readOnly) { if (isReadOnlySupported()) { ResourceAttributes resourceAttributes= resource.getResourceAttributes(); if (resourceAttributes == null) return; // not supported on this platform for this resource resourceAttributes.setReadOnly(readOnly); try { resource.setResourceAttributes(resourceAttributes); } catch (CoreException e) { // ignore } } } 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 Java elements based on their toStringWithAncestors(), returning a new array * with the sorted items. The original array is left untouched. */ public static IJavaElement[] sortCopy(IJavaElement[] elements) { int len= elements.length; IJavaElement[] copy= new IJavaElement[len]; System.arraycopy(elements, 0, copy, 0, len); sort(copy, new Comparer() { public int compare(Object a, Object b) { return ((JavaElement)a).toStringWithAncestors().compareTo(((JavaElement)b).toStringWithAncestors()); } }); 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; } /* * Returns whether the given compound name starts with the given prefix. * Returns true if the n first elements of the prefix are equals and the last element of the * prefix is a prefix of the corresponding element in the compound name. */ public static boolean startsWithIgnoreCase(String[] compoundName, String[] prefix, boolean partialMatch) { int prefixLength= prefix.length; int nameLength= compoundName.length; if (prefixLength > nameLength) return false; for (int i= 0; i < prefixLength - 1; i++) { if (!compoundName[i].equalsIgnoreCase(prefix[i])) return false; } return (partialMatch || prefixLength == nameLength) && compoundName[prefixLength - 1].toLowerCase().startsWith(prefix[prefixLength - 1].toLowerCase()); } /** * 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; } /** * Converts a String to char[][], where segments are separate by '.'. */ public static char[][] toCompoundChars(String s) { int len= s.length(); if (len == 0) { return CharOperation.NO_CHAR_CHAR; } int segCount= 1; for (int off= s.indexOf('.'); off != -1; off= s.indexOf('.', off + 1)) { ++segCount; } char[][] segs= new char[segCount][]; int start= 0; for (int i= 0; i < segCount; ++i) { int dot= s.indexOf('.', start); int end= (dot == -1 ? s.length() : dot); segs[i]= new char[end - start]; s.getChars(start, end, segs[i], 0); start= end + 1; } return segs; } /* * Converts the given URI to a local file. Use the existing file if the uri is on the local file system. * Otherwise fetch it. * Returns null if unable to fetch it. */ public static File toLocalFile(URI uri, IProgressMonitor monitor) throws CoreException { IFileStore fileStore= EFS.getStore(uri); File localFile= fileStore.toLocalFile(EFS.NONE, monitor); if (localFile == null) // non local file system localFile= fileStore.toLocalFile(EFS.CACHE, monitor); return localFile; } /** * 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(); } /** * Converts a char[][] and a char[] to String, where segments are separated by '.'. */ public static String toString(char[][] c, char[] d) { if (c == null) return new String(d); StringBuffer sb= new StringBuffer(); for (int i= 0, max= c.length; i < max; ++i) { sb.append(c[i]); sb.append('.'); } sb.append(d); return sb.toString(); } /* * Converts a char[][] to String[]. */ public static String[] toStrings(char[][] a) { int len= a.length; String[] result= new String[len]; for (int i= 0; i < len; ++i) { result[i]= new String(a[i]); } return result; } private static char[] toUnresolvedTypeSignature(char[] signature) { int length= signature.length; if (length <= 1) return signature; StringBuffer buffer= new StringBuffer(length); toUnresolvedTypeSignature(signature, 0, length, buffer); int bufferLength= buffer.length(); char[] result= new char[bufferLength]; buffer.getChars(0, bufferLength, result, 0); return result; } private static int toUnresolvedTypeSignature(char[] signature, int start, int length, StringBuffer buffer) { if (signature[start] == Signature.C_RESOLVED) buffer.append(Signature.C_UNRESOLVED); else buffer.append(signature[start]); for (int i= start + 1; i < length; i++) { char c= signature[i]; switch (c) { case '/': case Signature.C_DOLLAR: buffer.append(Signature.C_DOT); break; case Signature.C_GENERIC_START: buffer.append(Signature.C_GENERIC_START); i= toUnresolvedTypeSignature(signature, i + 1, length, buffer); break; case Signature.C_GENERIC_END: buffer.append(Signature.C_GENERIC_END); return i; default: buffer.append(c); break; } } return length; } 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= 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; } } public static String toString(char[] declaringClass, char[] methodName, char[] methodSignature, boolean includeReturnType, boolean compact) { final boolean isConstructor= CharOperation.equals(methodName, INIT); int firstParen= CharOperation.indexOf(Signature.C_PARAM_START, methodSignature); if (firstParen == -1) { return ""; //$NON-NLS-1$ } StringBuffer buffer= new StringBuffer(methodSignature.length + 10); // decode declaring class name // it can be either an array signature or a type signature if (declaringClass != null && declaringClass.length > 0) { char[] declaringClassSignature= null; if (declaringClass[0] == Signature.C_ARRAY) { CharOperation.replace(declaringClass, '/', '.'); declaringClassSignature= Signature.toCharArray(declaringClass); } else { CharOperation.replace(declaringClass, '/', '.'); declaringClassSignature= declaringClass; } int lastIndexOfSlash= CharOperation.lastIndexOf('.', declaringClassSignature); if (compact && lastIndexOfSlash != -1) { buffer.append(declaringClassSignature, lastIndexOfSlash + 1, declaringClassSignature.length - lastIndexOfSlash - 1); } else { buffer.append(declaringClassSignature); } if (!isConstructor) { buffer.append('.'); } } // selector if (!isConstructor && methodName != null) { buffer.append(methodName); } // parameters buffer.append('('); char[][] pts= Signature.getParameterTypes(methodSignature); for (int i= 0, max= pts.length; i < max; i++) { appendTypeSignature(pts[i], 0, buffer, compact); if (i != pts.length - 1) { buffer.append(','); buffer.append(' '); } } buffer.append(')'); if (!isConstructor) { buffer.append(" : "); //$NON-NLS-1$ // return type if (includeReturnType) { char[] rts= Signature.getReturnType(methodSignature); appendTypeSignature(rts, 0, buffer, compact); } } return String.valueOf(buffer); } /* * 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) { char[][] compoundName= type.getParameterizedTypeName(); char[] typeName= CharOperation.concatWith(compoundName, '.'); String 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)); } public static void verbose(String log) { verbose(log, System.out); } public static synchronized void verbose(String log, PrintStream printStream) { int start= 0; do { int end= log.indexOf('\n', start); printStream.print(Thread.currentThread()); printStream.print(" "); //$NON-NLS-1$ printStream.print(log.substring(start, end == -1 ? log.length() : end + 1)); start= end + 1; } while (start != 0); printStream.println(); } /** * 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; } /** * 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(char[] fileName) { if (fileName == null) return false; 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; if (extensionStart - 1 < 0) continue; if (fileName[extensionStart - 1] != '.') continue; for (int j= 0; j < extensionLength; j++) { if (fileName[extensionStart + j] != extension[j]) continue extensions; } return true; } return false; } /** * Scans the given string for a type signature starting at the given index and returns the index * of the last character. * * <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 * @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 new IllegalArgumentException(); } char c= string[start]; switch (c) { case Signature.C_ARRAY: return scanArrayTypeSignature(string, start); case Signature.C_RESOLVED: case Signature.C_UNRESOLVED: return scanClassTypeSignature(string, start); case Signature.C_TYPE_VARIABLE: return scanTypeVariableSignature(string, start); 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: return scanBaseTypeSignature(string, start); case Signature.C_CAPTURE: return scanCaptureTypeSignature(string, start); case Signature.C_EXTENDS: case Signature.C_SUPER: case Signature.C_STAR: return scanTypeBoundSignature(string, start); default: throw new IllegalArgumentException(); } } /** * Scans the given string for a base type signature starting at the given index and returns the * index of the last character. * * <pre> * BaseTypeSignature: * <b>B</b> | <b>C</b> | <b>D</b> | <b>F</b> | <b>I</b> * | <b>J</b> | <b>S</b> | <b>V</b> | <b>Z</b> * </pre> * * 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 new IllegalArgumentException(); } char c= string[start]; if ("BCDFIJSVZ".indexOf(c) >= 0) { //$NON-NLS-1$ return start; } else { throw new IllegalArgumentException(); } } /** * Scans the given string for an array type signature starting at the given index and returns * the index of the last character. * * <pre> * ArrayTypeSignature: * <b>[</b> TypeSignature * </pre> * * @param string the signature string * @param start the 0-based character index of the first character * @return the 0-based character index of the last character * @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 new IllegalArgumentException(); } char c= string[start]; if (c != Signature.C_ARRAY) { throw new IllegalArgumentException(); } c= string[++start]; while (c == Signature.C_ARRAY) { // need a minimum 2 char if (start >= length - 1) { throw new IllegalArgumentException(); } c= string[++start]; } return scanTypeSignature(string, start); } /** * Scans the given string for a capture of a wildcard type signature starting at the given index * and returns the index of the last character. * * <pre> * CaptureTypeSignature: * <b>!</b> TypeBoundSignature * </pre> * * @param string the signature string * @param start the 0-based character index of the first character * @return the 0-based character index of the last character * @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 new IllegalArgumentException(); } char c= string[start]; if (c != Signature.C_CAPTURE) { throw new IllegalArgumentException(); } return scanTypeBoundSignature(string, start + 1); } /** * Scans the given string for a type variable signature starting at the given index and returns * the index of the last character. * * <pre> * TypeVariableSignature: * <b>T</b> Identifier <b>;</b> * </pre> * * @param string the signature string * @param start the 0-based character index of the first character * @return the 0-based character index of the last character * @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 new IllegalArgumentException(); } // must start in "T" char c= string[start]; if (c != Signature.C_TYPE_VARIABLE) { throw new IllegalArgumentException(); } int id= scanIdentifier(string, start + 1); c= string[id + 1]; if (c == Signature.C_SEMICOLON) { return id + 1; } else { throw new IllegalArgumentException(); } } /** * Scans the given string for an identifier starting at the given index and returns the index of * the last character. Stop characters are: ";", ":", "<", ">", "/", ".". * * @param string the signature string * @param start the 0-based character index of the first character * @return the 0-based character index of the last character * @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 new IllegalArgumentException(); } int p= start; while (true) { char c= string[p]; if (c == '<' || c == '>' || c == ':' || c == ';' || c == '.' || c == '/') { return p - 1; } p++; if (p == string.length) { return p - 1; } } } /** * Scans the given string for a class type signature starting at the given index and returns the * index of the last character. * * <pre> * ClassTypeSignature: * { <b>L</b> | <b>Q</b> } Identifier * { { <b>/</b> | <b>.</b> Identifier [ <b><</b> TypeArgumentSignature* <b>></b> ] } * <b>;</b> * </pre> * * 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 new IllegalArgumentException(); } // must start in "L" or "Q" char c= string[start]; if (c != Signature.C_RESOLVED && c != Signature.C_UNRESOLVED) { return -1; } int p= start + 1; while (true) { if (p >= string.length) { throw new IllegalArgumentException(); } c= string[p]; if (c == Signature.C_SEMICOLON) { // all done return p; } else if (c == Signature.C_GENERIC_START) { int e= scanTypeArgumentSignatures(string, p); p= e; } else if (c == Signature.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. * * <pre> * TypeBoundSignature: * <b>[-+]</b> TypeSignature <b>;</b> * <b>*</b></b> * </pre> * * @param string the signature string * @param start the 0-based character index of the first character * @return the 0-based character index of the last character * @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 new IllegalArgumentException(); } char c= string[start]; switch (c) { case Signature.C_STAR: return start; case Signature.C_SUPER: case Signature.C_EXTENDS: // need a minimum 3 chars "+[I" if (start >= string.length - 2) { throw new IllegalArgumentException(); } break; default: // must start in "+/-" throw new IllegalArgumentException(); } c= string[++start]; switch (c) { case Signature.C_CAPTURE: return scanCaptureTypeSignature(string, start); case Signature.C_SUPER: case Signature.C_EXTENDS: return scanTypeBoundSignature(string, start); case Signature.C_RESOLVED: case Signature.C_UNRESOLVED: return scanClassTypeSignature(string, start); case Signature.C_TYPE_VARIABLE: return scanTypeVariableSignature(string, start); case Signature.C_ARRAY: return scanArrayTypeSignature(string, start); case Signature.C_STAR: return start; default: throw new IllegalArgumentException(); } } /** * Scans the given string for a list of type argument signatures starting at the given index and * returns the index of the last character. * * <pre> * TypeArgumentSignatures: * <b><</b> TypeArgumentSignature* <b>></b> * </pre> * * 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 new IllegalArgumentException(); } char c= string[start]; if (c != Signature.C_GENERIC_START) { throw new IllegalArgumentException(); } int p= start + 1; while (true) { if (p >= string.length) { throw new IllegalArgumentException(); } c= string[p]; if (c == Signature.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. * * <pre> * TypeArgumentSignature: * <b>*</b> * | <b>+</b> TypeSignature * | <b>-</b> TypeSignature * | TypeSignature * </pre> * * 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 new IllegalArgumentException(); } char c= string[start]; switch (c) { case Signature.C_STAR: return start; case Signature.C_EXTENDS: case Signature.C_SUPER: return scanTypeBoundSignature(string, start); default: return scanTypeSignature(string, start); } } /** * Get all type arguments from an array of signatures. * * 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',';','>',';'] ] * * @see #splitTypeLevelsSignature(String) Then, this method returns: [ [ * ['L','Y','<','L','Z',';'>',';'], ['L','V','<','L','W',';'>',';'], ['L','U',';'] ], [ * ['L','B',';'] ] ] * * @param typeSignatures Array of signatures (one per each type levels) * @throws IllegalArgumentException If one of provided signature is malformed * @return char[][][] Array of type arguments for each signature */ 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; } public static IAnnotation getAnnotation(JavaElement parent, IBinaryAnnotation binaryAnnotation, String memberValuePairName) { char[] typeName= org.eclipse.jdt.core.Signature.toCharArray(CharOperation.replaceOnCopy(binaryAnnotation.getTypeName(), '/', '.')); return new Annotation(parent, new String(typeName), memberValuePairName); } public static Object getAnnotationMemberValue(JavaElement parent, 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, (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, 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; } } /** * Split signatures of all levels from a type unique key. * * Example: For following type X<Y<Z>,V<W>,U>.A<B>, unique key is: * "LX<LY<LZ;>;LV<LW;>;LU;>.LA<LB;>;" * * 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 typeArgsCount= 0; 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); } typeArgsCount= 0; } break; case '<': paramOpening++; if (paramOpening == 1) { typeArgsCount= 1; } break; case '*': case ';': if (paramOpening == 1) typeArgsCount++; 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--) { typeSignatures[i]= signatures[j]; } return typeSignatures; } /* * Can throw IllegalArgumentException or ArrayIndexOutOfBoundsException */ public static String toAnchor(int startingIndex, char[] methodSignature, String methodName, boolean isVarArgs) { try { return new String(toAnchor(startingIndex, methodSignature, methodName.toCharArray(), isVarArgs)); } catch (IllegalArgumentException e) { return null; } } 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= 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; } /* * This method adjusts the task tags and task priorities so that they have the same size */ public static void fixTaskTags(Map defaultOptionsMap) { Object taskTagsValue= defaultOptionsMap.get(JavaCore.COMPILER_TASK_TAGS); char[][] taskTags= null; if (taskTagsValue instanceof String) { taskTags= CharOperation.splitAndTrimOn(',', ((String)taskTagsValue).toCharArray()); } Object taskPrioritiesValue= defaultOptionsMap.get(JavaCore.COMPILER_TASK_PRIORITIES); char[][] taskPriorities= null; if (taskPrioritiesValue instanceof String) { taskPriorities= CharOperation.splitAndTrimOn(',', ((String)taskPrioritiesValue).toCharArray()); } if (taskPriorities == null) { if (taskTags != null) { Util.logRepeatedMessage(TASK_PRIORITIES_PROBLEM, IStatus.ERROR, "Inconsistent values for taskTags (not null) and task priorities (null)"); //$NON-NLS-1$ defaultOptionsMap.remove(JavaCore.COMPILER_TASK_TAGS); } return; } else if (taskTags == null) { Util.logRepeatedMessage(TASK_PRIORITIES_PROBLEM, IStatus.ERROR, "Inconsistent values for taskTags (null) and task priorities (not null)"); //$NON-NLS-1$ defaultOptionsMap.remove(JavaCore.COMPILER_TASK_PRIORITIES); return; } int taskTagsLength= taskTags.length; int taskPrioritiesLength= taskPriorities.length; if (taskTagsLength != taskPrioritiesLength) { Util.logRepeatedMessage(TASK_PRIORITIES_PROBLEM, IStatus.ERROR, "Inconsistent values for taskTags and task priorities : length is different"); //$NON-NLS-1$ if (taskTagsLength > taskPrioritiesLength) { System.arraycopy(taskTags, 0, (taskTags= new char[taskPrioritiesLength][]), 0, taskPrioritiesLength); defaultOptionsMap.put(JavaCore.COMPILER_TASK_TAGS, new String(CharOperation.concatWith(taskTags, ','))); } else { System.arraycopy(taskPriorities, 0, (taskPriorities= new char[taskTagsLength][]), 0, taskTagsLength); defaultOptionsMap.put(JavaCore.COMPILER_TASK_PRIORITIES, new String(CharOperation.concatWith(taskPriorities, ','))); } } } }