0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.core.util;
0011:
0012: import java.io.*;
0013: import java.net.URI;
0014: import java.util.*;
0015: import java.util.zip.ZipEntry;
0016: import java.util.zip.ZipFile;
0017:
0018: import org.eclipse.core.filesystem.EFS;
0019: import org.eclipse.core.filesystem.IFileStore;
0020: import org.eclipse.core.resources.*;
0021: import org.eclipse.core.runtime.*;
0022: import org.eclipse.core.runtime.content.IContentType;
0023: import org.eclipse.core.runtime.preferences.IScopeContext;
0024: import org.eclipse.core.runtime.preferences.InstanceScope;
0025: import org.eclipse.jdt.core.*;
0026: import org.eclipse.jdt.core.compiler.CharOperation;
0027: import org.eclipse.jdt.core.dom.ASTNode;
0028: import org.eclipse.jdt.core.dom.ArrayType;
0029: import org.eclipse.jdt.core.dom.ParameterizedType;
0030: import org.eclipse.jdt.core.dom.PrimitiveType;
0031: import org.eclipse.jdt.core.dom.QualifiedType;
0032: import org.eclipse.jdt.core.dom.SimpleType;
0033: import org.eclipse.jdt.core.dom.Type;
0034: import org.eclipse.jdt.core.dom.WildcardType;
0035: import org.eclipse.jdt.core.util.IClassFileAttribute;
0036: import org.eclipse.jdt.core.util.IClassFileReader;
0037: import org.eclipse.jdt.core.util.ICodeAttribute;
0038: import org.eclipse.jdt.core.util.IFieldInfo;
0039: import org.eclipse.jdt.core.util.IMethodInfo;
0040: import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
0041: import org.eclipse.jdt.internal.compiler.ast.Argument;
0042: import org.eclipse.jdt.internal.compiler.ast.TypeReference;
0043: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
0044: import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
0045: import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
0046: import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
0047: import org.eclipse.jdt.internal.core.JavaElement;
0048: import org.eclipse.jdt.internal.core.JavaModelManager;
0049: import org.eclipse.jdt.internal.core.PackageFragmentRoot;
0050: import org.eclipse.jface.text.BadLocationException;
0051: import org.eclipse.text.edits.MalformedTreeException;
0052: import org.eclipse.text.edits.TextEdit;
0053:
0054: /**
0055: * Provides convenient utility methods to other types in this package.
0056: */
0057: public class Util {
0058:
0059: public interface Comparable {
0060: /**
0061: * Returns 0 if this and c are equal, >0 if this is greater than c,
0062: * or <0 if this is less than c.
0063: */
0064: int compareTo(Comparable c);
0065: }
0066:
0067: public interface Comparer {
0068: /**
0069: * Returns 0 if a and b are equal, >0 if a is greater than b,
0070: * or <0 if a is less than b.
0071: */
0072: int compare(Object a, Object b);
0073: }
0074:
0075: private static final String ARGUMENTS_DELIMITER = "#"; //$NON-NLS-1$
0076:
0077: private static final String EMPTY_ARGUMENT = " "; //$NON-NLS-1$
0078:
0079: private static char[][] JAVA_LIKE_EXTENSIONS;
0080: public static boolean ENABLE_JAVA_LIKE_EXTENSIONS = true;
0081:
0082: private static final char[] BOOLEAN = "boolean".toCharArray(); //$NON-NLS-1$
0083: private static final char[] BYTE = "byte".toCharArray(); //$NON-NLS-1$
0084: private static final char[] CHAR = "char".toCharArray(); //$NON-NLS-1$
0085: private static final char[] DOUBLE = "double".toCharArray(); //$NON-NLS-1$
0086: private static final char[] FLOAT = "float".toCharArray(); //$NON-NLS-1$
0087: private static final char[] INT = "int".toCharArray(); //$NON-NLS-1$
0088: private static final char[] LONG = "long".toCharArray(); //$NON-NLS-1$
0089: private static final char[] SHORT = "short".toCharArray(); //$NON-NLS-1$
0090: private static final char[] VOID = "void".toCharArray(); //$NON-NLS-1$
0091: private static final char[] INIT = "<init>".toCharArray(); //$NON-NLS-1$
0092:
0093: private Util() {
0094: // cannot be instantiated
0095: }
0096:
0097: /**
0098: * Returns a new array adding the second array at the end of first array.
0099: * It answers null if the first and second are null.
0100: * If the first array is null or if it is empty, then a new array is created with second.
0101: * If the second array is null, then the first array is returned.
0102: * <br>
0103: * <br>
0104: * For example:
0105: * <ol>
0106: * <li><pre>
0107: * first = null
0108: * second = "a"
0109: * => result = {"a"}
0110: * </pre>
0111: * <li><pre>
0112: * first = {"a"}
0113: * second = null
0114: * => result = {"a"}
0115: * </pre>
0116: * </li>
0117: * <li><pre>
0118: * first = {"a"}
0119: * second = {"b"}
0120: * => result = {"a", "b"}
0121: * </pre>
0122: * </li>
0123: * </ol>
0124: *
0125: * @param first the first array to concatenate
0126: * @param second the array to add at the end of the first array
0127: * @return a new array adding the second array at the end of first array, or null if the two arrays are null.
0128: */
0129: public static final String[] arrayConcat(String[] first,
0130: String second) {
0131: if (second == null)
0132: return first;
0133: if (first == null)
0134: return new String[] { second };
0135:
0136: int length = first.length;
0137: if (first.length == 0) {
0138: return new String[] { second };
0139: }
0140:
0141: String[] result = new String[length + 1];
0142: System.arraycopy(first, 0, result, 0, length);
0143: result[length] = second;
0144: return result;
0145: }
0146:
0147: /**
0148: * Checks the type signature in String sig,
0149: * starting at start and ending before end (end is not included).
0150: * Returns the index of the character immediately after the signature if valid,
0151: * or -1 if not valid.
0152: */
0153: private static int checkTypeSignature(String sig, int start,
0154: int end, boolean allowVoid) {
0155: if (start >= end)
0156: return -1;
0157: int i = start;
0158: char c = sig.charAt(i++);
0159: int nestingDepth = 0;
0160: while (c == '[') {
0161: ++nestingDepth;
0162: if (i >= end)
0163: return -1;
0164: c = sig.charAt(i++);
0165: }
0166: switch (c) {
0167: case 'B':
0168: case 'C':
0169: case 'D':
0170: case 'F':
0171: case 'I':
0172: case 'J':
0173: case 'S':
0174: case 'Z':
0175: break;
0176: case 'V':
0177: if (!allowVoid)
0178: return -1;
0179: // array of void is not allowed
0180: if (nestingDepth != 0)
0181: return -1;
0182: break;
0183: case 'L':
0184: int semicolon = sig.indexOf(';', i);
0185: // Must have at least one character between L and ;
0186: if (semicolon <= i || semicolon >= end)
0187: return -1;
0188: i = semicolon + 1;
0189: break;
0190: default:
0191: return -1;
0192: }
0193: return i;
0194: }
0195:
0196: /**
0197: * Combines two hash codes to make a new one.
0198: */
0199: public static int combineHashCodes(int hashCode1, int hashCode2) {
0200: return hashCode1 * 17 + hashCode2;
0201: }
0202:
0203: /**
0204: * Compares two byte arrays.
0205: * 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.
0206: * 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.
0207: * Returns 0 if they are equal or both null.
0208: */
0209: public static int compare(byte[] a, byte[] b) {
0210: if (a == b)
0211: return 0;
0212: if (a == null)
0213: return -1;
0214: if (b == null)
0215: return 1;
0216: int len = Math.min(a.length, b.length);
0217: for (int i = 0; i < len; ++i) {
0218: int diff = a[i] - b[i];
0219: if (diff != 0)
0220: return diff;
0221: }
0222: if (a.length > len)
0223: return 1;
0224: if (b.length > len)
0225: return -1;
0226: return 0;
0227: }
0228:
0229: /**
0230: * Compares two strings lexicographically.
0231: * The comparison is based on the Unicode value of each character in
0232: * the strings.
0233: *
0234: * @return the value <code>0</code> if the str1 is equal to str2;
0235: * a value less than <code>0</code> if str1
0236: * is lexicographically less than str2;
0237: * and a value greater than <code>0</code> if str1 is
0238: * lexicographically greater than str2.
0239: */
0240: public static int compare(char[] str1, char[] str2) {
0241: int len1 = str1.length;
0242: int len2 = str2.length;
0243: int n = Math.min(len1, len2);
0244: int i = 0;
0245: while (n-- != 0) {
0246: char c1 = str1[i];
0247: char c2 = str2[i++];
0248: if (c1 != c2) {
0249: return c1 - c2;
0250: }
0251: }
0252: return len1 - len2;
0253: }
0254:
0255: /**
0256: * Concatenate two strings with a char in between.
0257: * @see #concat(String, String)
0258: */
0259: public static String concat(String s1, char c, String s2) {
0260: if (s1 == null)
0261: s1 = "null"; //$NON-NLS-1$
0262: if (s2 == null)
0263: s2 = "null"; //$NON-NLS-1$
0264: int l1 = s1.length();
0265: int l2 = s2.length();
0266: char[] buf = new char[l1 + 1 + l2];
0267: s1.getChars(0, l1, buf, 0);
0268: buf[l1] = c;
0269: s2.getChars(0, l2, buf, l1 + 1);
0270: return new String(buf);
0271: }
0272:
0273: /**
0274: * Concatenate two strings.
0275: * Much faster than using +, which:
0276: * - creates a StringBuffer,
0277: * - which is synchronized,
0278: * - of default size, so the resulting char array is
0279: * often larger than needed.
0280: * This implementation creates an extra char array, since the
0281: * String constructor copies its argument, but there's no way around this.
0282: */
0283: public static String concat(String s1, String s2) {
0284: if (s1 == null)
0285: s1 = "null"; //$NON-NLS-1$
0286: if (s2 == null)
0287: s2 = "null"; //$NON-NLS-1$
0288: int l1 = s1.length();
0289: int l2 = s2.length();
0290: char[] buf = new char[l1 + l2];
0291: s1.getChars(0, l1, buf, 0);
0292: s2.getChars(0, l2, buf, l1);
0293: return new String(buf);
0294: }
0295:
0296: /**
0297: * Returns the concatenation of the given array parts using the given separator between each part.
0298: * <br>
0299: * <br>
0300: * For example:<br>
0301: * <ol>
0302: * <li><pre>
0303: * array = {"a", "b"}
0304: * separator = '.'
0305: * => result = "a.b"
0306: * </pre>
0307: * </li>
0308: * <li><pre>
0309: * array = {}
0310: * separator = '.'
0311: * => result = ""
0312: * </pre></li>
0313: * </ol>
0314: *
0315: * @param array the given array
0316: * @param separator the given separator
0317: * @return the concatenation of the given array parts using the given separator between each part
0318: */
0319: public static final String concatWith(String[] array, char separator) {
0320: StringBuffer buffer = new StringBuffer();
0321: for (int i = 0, length = array.length; i < length; i++) {
0322: buffer.append(array[i]);
0323: if (i < length - 1)
0324: buffer.append(separator);
0325: }
0326: return buffer.toString();
0327: }
0328:
0329: /**
0330: * Returns the concatenation of the given array parts using the given separator between each
0331: * part and appending the given name at the end.
0332: * <br>
0333: * <br>
0334: * For example:<br>
0335: * <ol>
0336: * <li><pre>
0337: * name = "c"
0338: * array = { "a", "b" }
0339: * separator = '.'
0340: * => result = "a.b.c"
0341: * </pre>
0342: * </li>
0343: * <li><pre>
0344: * name = null
0345: * array = { "a", "b" }
0346: * separator = '.'
0347: * => result = "a.b"
0348: * </pre></li>
0349: * <li><pre>
0350: * name = " c"
0351: * array = null
0352: * separator = '.'
0353: * => result = "c"
0354: * </pre></li>
0355: * </ol>
0356: *
0357: * @param array the given array
0358: * @param name the given name
0359: * @param separator the given separator
0360: * @return the concatenation of the given array parts using the given separator between each
0361: * part and appending the given name at the end
0362: */
0363: public static final String concatWith(String[] array, String name,
0364: char separator) {
0365:
0366: if (array == null || array.length == 0)
0367: return name;
0368: if (name == null || name.length() == 0)
0369: return concatWith(array, separator);
0370: StringBuffer buffer = new StringBuffer();
0371: for (int i = 0, length = array.length; i < length; i++) {
0372: buffer.append(array[i]);
0373: buffer.append(separator);
0374: }
0375: buffer.append(name);
0376: return buffer.toString();
0377:
0378: }
0379:
0380: /**
0381: * Concatenate three strings.
0382: * @see #concat(String, String)
0383: */
0384: public static String concat(String s1, String s2, String s3) {
0385: if (s1 == null)
0386: s1 = "null"; //$NON-NLS-1$
0387: if (s2 == null)
0388: s2 = "null"; //$NON-NLS-1$
0389: if (s3 == null)
0390: s3 = "null"; //$NON-NLS-1$
0391: int l1 = s1.length();
0392: int l2 = s2.length();
0393: int l3 = s3.length();
0394: char[] buf = new char[l1 + l2 + l3];
0395: s1.getChars(0, l1, buf, 0);
0396: s2.getChars(0, l2, buf, l1);
0397: s3.getChars(0, l3, buf, l1 + l2);
0398: return new String(buf);
0399: }
0400:
0401: /**
0402: * Converts a type signature from the IBinaryType representation to the DC representation.
0403: */
0404: public static String convertTypeSignature(char[] sig, int start,
0405: int length) {
0406: return new String(sig, start, length).replace('/', '.');
0407: }
0408:
0409: /*
0410: * Returns the default java extension (".java").
0411: * To be used when the extension is not known.
0412: */
0413: public static String defaultJavaExtension() {
0414: return SuffixConstants.SUFFIX_STRING_java;
0415: }
0416:
0417: /**
0418: * Apply the given edit on the given string and return the updated string.
0419: * Return the given string if anything wrong happen while applying the edit.
0420: *
0421: * @param original the given string
0422: * @param edit the given edit
0423: *
0424: * @return the updated string
0425: */
0426: public final static String editedString(String original,
0427: TextEdit edit) {
0428: if (edit == null) {
0429: return original;
0430: }
0431: SimpleDocument document = new SimpleDocument(original);
0432: try {
0433: edit.apply(document, TextEdit.NONE);
0434: return document.get();
0435: } catch (MalformedTreeException e) {
0436: e.printStackTrace();
0437: } catch (BadLocationException e) {
0438: e.printStackTrace();
0439: }
0440: return original;
0441: }
0442:
0443: /**
0444: * Returns true iff str.toLowerCase().endsWith(end.toLowerCase())
0445: * implementation is not creating extra strings.
0446: */
0447: public final static boolean endsWithIgnoreCase(String str,
0448: String end) {
0449:
0450: int strLength = str == null ? 0 : str.length();
0451: int endLength = end == null ? 0 : end.length();
0452:
0453: // return false if the string is smaller than the end.
0454: if (endLength > strLength)
0455: return false;
0456:
0457: // return false if any character of the end are
0458: // not the same in lower case.
0459: for (int i = 1; i <= endLength; i++) {
0460: if (ScannerHelper.toLowerCase(end.charAt(endLength - i)) != ScannerHelper
0461: .toLowerCase(str.charAt(strLength - i)))
0462: return false;
0463: }
0464:
0465: return true;
0466: }
0467:
0468: /**
0469: * Compares two arrays using equals() on the elements.
0470: * Neither can be null. Only the first len elements are compared.
0471: * Return false if either array is shorter than len.
0472: */
0473: public static boolean equalArrays(Object[] a, Object[] b, int len) {
0474: if (a == b)
0475: return true;
0476: if (a.length < len || b.length < len)
0477: return false;
0478: for (int i = 0; i < len; ++i) {
0479: if (a[i] == null) {
0480: if (b[i] != null)
0481: return false;
0482: } else {
0483: if (!a[i].equals(b[i]))
0484: return false;
0485: }
0486: }
0487: return true;
0488: }
0489:
0490: /**
0491: * Compares two arrays using equals() on the elements.
0492: * Either or both arrays may be null.
0493: * Returns true if both are null.
0494: * Returns false if only one is null.
0495: * If both are arrays, returns true iff they have the same length and
0496: * all elements are equal.
0497: */
0498: public static boolean equalArraysOrNull(int[] a, int[] b) {
0499: if (a == b)
0500: return true;
0501: if (a == null || b == null)
0502: return false;
0503: int len = a.length;
0504: if (len != b.length)
0505: return false;
0506: for (int i = 0; i < len; ++i) {
0507: if (a[i] != b[i])
0508: return false;
0509: }
0510: return true;
0511: }
0512:
0513: /**
0514: * Compares two arrays using equals() on the elements.
0515: * Either or both arrays may be null.
0516: * Returns true if both are null.
0517: * Returns false if only one is null.
0518: * If both are arrays, returns true iff they have the same length and
0519: * all elements compare true with equals.
0520: */
0521: public static boolean equalArraysOrNull(Object[] a, Object[] b) {
0522: if (a == b)
0523: return true;
0524: if (a == null || b == null)
0525: return false;
0526:
0527: int len = a.length;
0528: if (len != b.length)
0529: return false;
0530: // walk array from end to beginning as this optimizes package name cases
0531: // where the first part is always the same (e.g. org.eclipse.jdt)
0532: for (int i = len - 1; i >= 0; i--) {
0533: if (a[i] == null) {
0534: if (b[i] != null)
0535: return false;
0536: } else {
0537: if (!a[i].equals(b[i]))
0538: return false;
0539: }
0540: }
0541: return true;
0542: }
0543:
0544: /**
0545: * Compares two arrays using equals() on the elements.
0546: * The arrays are first sorted.
0547: * Either or both arrays may be null.
0548: * Returns true if both are null.
0549: * Returns false if only one is null.
0550: * If both are arrays, returns true iff they have the same length and
0551: * iff, after sorting both arrays, all elements compare true with equals.
0552: * The original arrays are left untouched.
0553: */
0554: public static boolean equalArraysOrNullSortFirst(Comparable[] a,
0555: Comparable[] b) {
0556: if (a == b)
0557: return true;
0558: if (a == null || b == null)
0559: return false;
0560: int len = a.length;
0561: if (len != b.length)
0562: return false;
0563: if (len >= 2) { // only need to sort if more than two items
0564: a = sortCopy(a);
0565: b = sortCopy(b);
0566: }
0567: for (int i = 0; i < len; ++i) {
0568: if (!a[i].equals(b[i]))
0569: return false;
0570: }
0571: return true;
0572: }
0573:
0574: /**
0575: * Compares two String arrays using equals() on the elements.
0576: * The arrays are first sorted.
0577: * Either or both arrays may be null.
0578: * Returns true if both are null.
0579: * Returns false if only one is null.
0580: * If both are arrays, returns true iff they have the same length and
0581: * iff, after sorting both arrays, all elements compare true with equals.
0582: * The original arrays are left untouched.
0583: */
0584: public static boolean equalArraysOrNullSortFirst(String[] a,
0585: String[] b) {
0586: if (a == b)
0587: return true;
0588: if (a == null || b == null)
0589: return false;
0590: int len = a.length;
0591: if (len != b.length)
0592: return false;
0593: if (len >= 2) { // only need to sort if more than two items
0594: a = sortCopy(a);
0595: b = sortCopy(b);
0596: }
0597: for (int i = 0; i < len; ++i) {
0598: if (!a[i].equals(b[i]))
0599: return false;
0600: }
0601: return true;
0602: }
0603:
0604: /**
0605: * Compares two objects using equals().
0606: * Either or both array may be null.
0607: * Returns true if both are null.
0608: * Returns false if only one is null.
0609: * Otherwise, return the result of comparing with equals().
0610: */
0611: public static boolean equalOrNull(Object a, Object b) {
0612: if (a == b) {
0613: return true;
0614: }
0615: if (a == null || b == null) {
0616: return false;
0617: }
0618: return a.equals(b);
0619: }
0620:
0621: /*
0622: * Returns whether the given file name equals to the given string ignoring the java like extension
0623: * of the file name.
0624: * Returns false if it is not a java like file name.
0625: */
0626: public static boolean equalsIgnoreJavaLikeExtension(
0627: String fileName, String string) {
0628: int fileNameLength = fileName.length();
0629: int stringLength = string.length();
0630: if (fileNameLength < stringLength)
0631: return false;
0632: for (int i = 0; i < stringLength; i++) {
0633: if (fileName.charAt(i) != string.charAt(i)) {
0634: return false;
0635: }
0636: }
0637: char[][] javaLikeExtensions = getJavaLikeExtensions();
0638: suffixes: for (int i = 0, length = javaLikeExtensions.length; i < length; i++) {
0639: char[] suffix = javaLikeExtensions[i];
0640: int extensionStart = stringLength + 1;
0641: if (extensionStart + suffix.length != fileNameLength)
0642: continue;
0643: if (fileName.charAt(stringLength) != '.')
0644: continue;
0645: for (int j = extensionStart; j < fileNameLength; j++) {
0646: if (fileName.charAt(j) != suffix[j - extensionStart])
0647: continue suffixes;
0648: }
0649: return true;
0650: }
0651: return false;
0652: }
0653:
0654: /**
0655: * Given a qualified name, extract the last component.
0656: * If the input is not qualified, the same string is answered.
0657: */
0658: public static String extractLastName(String qualifiedName) {
0659: int i = qualifiedName.lastIndexOf('.');
0660: if (i == -1)
0661: return qualifiedName;
0662: return qualifiedName.substring(i + 1);
0663: }
0664:
0665: /**
0666: * Extracts the parameter types from a method signature.
0667: */
0668: public static String[] extractParameterTypes(char[] sig) {
0669: int count = getParameterCount(sig);
0670: String[] result = new String[count];
0671: if (count == 0)
0672: return result;
0673: int i = CharOperation.indexOf('(', sig) + 1;
0674: count = 0;
0675: int len = sig.length;
0676: int start = i;
0677: for (;;) {
0678: if (i == len)
0679: break;
0680: char c = sig[i];
0681: if (c == ')')
0682: break;
0683: if (c == '[') {
0684: ++i;
0685: } else if (c == 'L') {
0686: i = CharOperation.indexOf(';', sig, i + 1) + 1;
0687: Assert.isTrue(i != 0);
0688: result[count++] = convertTypeSignature(sig, start, i
0689: - start);
0690: start = i;
0691: } else {
0692: ++i;
0693: result[count++] = convertTypeSignature(sig, start, i
0694: - start);
0695: start = i;
0696: }
0697: }
0698: return result;
0699: }
0700:
0701: /**
0702: * Extracts the return type from a method signature.
0703: */
0704: public static String extractReturnType(String sig) {
0705: int i = sig.lastIndexOf(')');
0706: Assert.isTrue(i != -1);
0707: return sig.substring(i + 1);
0708: }
0709:
0710: private static IFile findFirstClassFile(IFolder folder) {
0711: try {
0712: IResource[] members = folder.members();
0713: for (int i = 0, max = members.length; i < max; i++) {
0714: IResource member = members[i];
0715: if (member.getType() == IResource.FOLDER) {
0716: return findFirstClassFile((IFolder) member);
0717: } else if (org.eclipse.jdt.internal.compiler.util.Util
0718: .isClassFileName(member.getName())) {
0719: return (IFile) member;
0720: }
0721: }
0722: } catch (CoreException e) {
0723: // ignore
0724: }
0725: return null;
0726: }
0727:
0728: /**
0729: * Finds the first line separator used by the given text.
0730: *
0731: * @return </code>"\n"</code> or </code>"\r"</code> or </code>"\r\n"</code>,
0732: * or <code>null</code> if none found
0733: */
0734: public static String findLineSeparator(char[] text) {
0735: // find the first line separator
0736: int length = text.length;
0737: if (length > 0) {
0738: char nextChar = text[0];
0739: for (int i = 0; i < length; i++) {
0740: char currentChar = nextChar;
0741: nextChar = i < length - 1 ? text[i + 1] : ' ';
0742: switch (currentChar) {
0743: case '\n':
0744: return "\n"; //$NON-NLS-1$
0745: case '\r':
0746: return nextChar == '\n' ? "\r\n" : "\r"; //$NON-NLS-1$ //$NON-NLS-2$
0747: }
0748: }
0749: }
0750: // not found
0751: return null;
0752: }
0753:
0754: public static IClassFileAttribute getAttribute(
0755: IClassFileReader classFileReader, char[] attributeName) {
0756: IClassFileAttribute[] attributes = classFileReader
0757: .getAttributes();
0758: for (int i = 0, max = attributes.length; i < max; i++) {
0759: if (CharOperation.equals(attributes[i].getAttributeName(),
0760: attributeName)) {
0761: return attributes[i];
0762: }
0763: }
0764: return null;
0765: }
0766:
0767: public static IClassFileAttribute getAttribute(
0768: ICodeAttribute codeAttribute, char[] attributeName) {
0769: IClassFileAttribute[] attributes = codeAttribute
0770: .getAttributes();
0771: for (int i = 0, max = attributes.length; i < max; i++) {
0772: if (CharOperation.equals(attributes[i].getAttributeName(),
0773: attributeName)) {
0774: return attributes[i];
0775: }
0776: }
0777: return null;
0778: }
0779:
0780: public static IClassFileAttribute getAttribute(
0781: IFieldInfo fieldInfo, char[] attributeName) {
0782: IClassFileAttribute[] attributes = fieldInfo.getAttributes();
0783: for (int i = 0, max = attributes.length; i < max; i++) {
0784: if (CharOperation.equals(attributes[i].getAttributeName(),
0785: attributeName)) {
0786: return attributes[i];
0787: }
0788: }
0789: return null;
0790: }
0791:
0792: public static IClassFileAttribute getAttribute(
0793: IMethodInfo methodInfo, char[] attributeName) {
0794: IClassFileAttribute[] attributes = methodInfo.getAttributes();
0795: for (int i = 0, max = attributes.length; i < max; i++) {
0796: if (CharOperation.equals(attributes[i].getAttributeName(),
0797: attributeName)) {
0798: return attributes[i];
0799: }
0800: }
0801: return null;
0802: }
0803:
0804: /**
0805: * Returns the registered Java like extensions.
0806: */
0807: public static char[][] getJavaLikeExtensions() {
0808: if (JAVA_LIKE_EXTENSIONS == null) {
0809: // TODO (jerome) reenable once JDT UI supports other file extensions (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=71460)
0810: if (!ENABLE_JAVA_LIKE_EXTENSIONS)
0811: JAVA_LIKE_EXTENSIONS = new char[][] { SuffixConstants.EXTENSION_java
0812: .toCharArray() };
0813: else {
0814: IContentType javaContentType = Platform
0815: .getContentTypeManager().getContentType(
0816: JavaCore.JAVA_SOURCE_CONTENT_TYPE);
0817: HashSet fileExtensions = new HashSet();
0818: // content types derived from java content type should be included (https://bugs.eclipse.org/bugs/show_bug.cgi?id=121715)
0819: IContentType[] contentTypes = Platform
0820: .getContentTypeManager().getAllContentTypes();
0821: for (int i = 0, length = contentTypes.length; i < length; i++) {
0822: if (contentTypes[i].isKindOf(javaContentType)) { // note that javaContentType.isKindOf(javaContentType) == true
0823: String[] fileExtension = contentTypes[i]
0824: .getFileSpecs(IContentType.FILE_EXTENSION_SPEC);
0825: for (int j = 0, length2 = fileExtension.length; j < length2; j++) {
0826: fileExtensions.add(fileExtension[j]);
0827: }
0828: }
0829: }
0830: int length = fileExtensions.size();
0831: // note that file extensions contains "java" as it is defined in JDT Core's plugin.xml
0832: char[][] extensions = new char[length][];
0833: extensions[0] = SuffixConstants.EXTENSION_java
0834: .toCharArray(); // ensure that "java" is first
0835: int index = 1;
0836: Iterator iterator = fileExtensions.iterator();
0837: while (iterator.hasNext()) {
0838: String fileExtension = (String) iterator.next();
0839: if (SuffixConstants.EXTENSION_java
0840: .equals(fileExtension))
0841: continue;
0842: extensions[index++] = fileExtension.toCharArray();
0843: }
0844: JAVA_LIKE_EXTENSIONS = extensions;
0845: }
0846: }
0847: return JAVA_LIKE_EXTENSIONS;
0848: }
0849:
0850: /**
0851: * Get the jdk level of this root.
0852: * The value can be:
0853: * <ul>
0854: * <li>major<<16 + minor : see predefined constants on ClassFileConstants </li>
0855: * <li><code>0</null> if the root is a source package fragment root or if a Java model exception occured</li>
0856: * </ul>
0857: * Returns the jdk level
0858: */
0859: public static long getJdkLevel(Object targetLibrary) {
0860: try {
0861: ClassFileReader reader = null;
0862: if (targetLibrary instanceof IFolder) {
0863: IFile classFile = findFirstClassFile((IFolder) targetLibrary); // only internal classfolders are allowed
0864: if (classFile != null)
0865: reader = Util.newClassFileReader(classFile);
0866: } else {
0867: // root is a jar file or a zip file
0868: ZipFile jar = null;
0869: try {
0870: IPath path = null;
0871: if (targetLibrary instanceof IResource) {
0872: path = ((IResource) targetLibrary)
0873: .getFullPath();
0874: } else if (targetLibrary instanceof File) {
0875: File f = (File) targetLibrary;
0876: if (!f.isDirectory()) {
0877: path = new Path(((File) targetLibrary)
0878: .getPath());
0879: }
0880: }
0881: if (path != null) {
0882: jar = JavaModelManager.getJavaModelManager()
0883: .getZipFile(path);
0884: for (Enumeration e = jar.entries(); e
0885: .hasMoreElements();) {
0886: ZipEntry member = (ZipEntry) e
0887: .nextElement();
0888: String entryName = member.getName();
0889: if (org.eclipse.jdt.internal.compiler.util.Util
0890: .isClassFileName(entryName)) {
0891: reader = ClassFileReader.read(jar,
0892: entryName);
0893: break;
0894: }
0895: }
0896: }
0897: } catch (CoreException e) {
0898: // ignore
0899: } finally {
0900: JavaModelManager.getJavaModelManager()
0901: .closeZipFile(jar);
0902: }
0903: }
0904: if (reader != null) {
0905: return reader.getVersion();
0906: }
0907: } catch (CoreException e) {
0908: // ignore
0909: } catch (ClassFormatException e) {
0910: // ignore
0911: } catch (IOException e) {
0912: // ignore
0913: }
0914: return 0;
0915: }
0916:
0917: /**
0918: * Returns the substring of the given file name, ending at the start of a
0919: * Java like extension. The entire file name is returned if it doesn't end
0920: * with a Java like extension.
0921: */
0922: public static String getNameWithoutJavaLikeExtension(String fileName) {
0923: int index = indexOfJavaLikeExtension(fileName);
0924: if (index == -1)
0925: return fileName;
0926: return fileName.substring(0, index);
0927: }
0928:
0929: /**
0930: * Returns the line separator found in the given text.
0931: * If it is null, or not found return the line delimitor for the given project.
0932: * If the project is null, returns the line separator for the workspace.
0933: * If still null, return the system line separator.
0934: */
0935: public static String getLineSeparator(String text,
0936: IJavaProject project) {
0937: String lineSeparator = null;
0938:
0939: // line delimiter in given text
0940: if (text != null && text.length() != 0) {
0941: lineSeparator = findLineSeparator(text.toCharArray());
0942: if (lineSeparator != null)
0943: return lineSeparator;
0944: }
0945:
0946: // line delimiter in project preference
0947: IScopeContext[] scopeContext;
0948: if (project != null) {
0949: scopeContext = new IScopeContext[] { new ProjectScope(
0950: project.getProject()) };
0951: lineSeparator = Platform.getPreferencesService().getString(
0952: Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR,
0953: null, scopeContext);
0954: if (lineSeparator != null)
0955: return lineSeparator;
0956: }
0957:
0958: // line delimiter in workspace preference
0959: scopeContext = new IScopeContext[] { new InstanceScope() };
0960: lineSeparator = Platform.getPreferencesService().getString(
0961: Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR,
0962: null, scopeContext);
0963: if (lineSeparator != null)
0964: return lineSeparator;
0965:
0966: // system line delimiter
0967: return org.eclipse.jdt.internal.compiler.util.Util.LINE_SEPARATOR;
0968: }
0969:
0970: /**
0971: * Returns the line separator used by the given buffer.
0972: * Uses the given text if none found.
0973: *
0974: * @return </code>"\n"</code> or </code>"\r"</code> or </code>"\r\n"</code>
0975: */
0976: private static String getLineSeparator(char[] text, char[] buffer) {
0977: // search in this buffer's contents first
0978: String lineSeparator = findLineSeparator(buffer);
0979: if (lineSeparator == null) {
0980: // search in the given text
0981: lineSeparator = findLineSeparator(text);
0982: if (lineSeparator == null) {
0983: // default to system line separator
0984: return getLineSeparator((String) null,
0985: (IJavaProject) null);
0986: }
0987: }
0988: return lineSeparator;
0989: }
0990:
0991: /**
0992: * Returns the number of parameter types in a method signature.
0993: */
0994: public static int getParameterCount(char[] sig) {
0995: int i = CharOperation.indexOf('(', sig) + 1;
0996: Assert.isTrue(i != 0);
0997: int count = 0;
0998: int len = sig.length;
0999: for (;;) {
1000: if (i == len)
1001: break;
1002: char c = sig[i];
1003: if (c == ')')
1004: break;
1005: if (c == '[') {
1006: ++i;
1007: } else if (c == 'L') {
1008: ++count;
1009: i = CharOperation.indexOf(';', sig, i + 1) + 1;
1010: Assert.isTrue(i != 0);
1011: } else {
1012: ++count;
1013: ++i;
1014: }
1015: }
1016: return count;
1017: }
1018:
1019: /**
1020: * Put all the arguments in one String.
1021: */
1022: public static String getProblemArgumentsForMarker(String[] arguments) {
1023: StringBuffer args = new StringBuffer(10);
1024:
1025: args.append(arguments.length);
1026: args.append(':');
1027:
1028: for (int j = 0; j < arguments.length; j++) {
1029: if (j != 0)
1030: args.append(ARGUMENTS_DELIMITER);
1031:
1032: if (arguments[j].length() == 0) {
1033: args.append(EMPTY_ARGUMENT);
1034: } else {
1035: args.append(arguments[j]);
1036: }
1037: }
1038:
1039: return args.toString();
1040: }
1041:
1042: /**
1043: * Separate all the arguments of a String made by getProblemArgumentsForMarker
1044: */
1045: public static String[] getProblemArgumentsFromMarker(
1046: String argumentsString) {
1047: if (argumentsString == null)
1048: return null;
1049: int index = argumentsString.indexOf(':');
1050: if (index == -1)
1051: return null;
1052:
1053: int length = argumentsString.length();
1054: int numberOfArg;
1055: try {
1056: numberOfArg = Integer.parseInt(argumentsString.substring(0,
1057: index));
1058: } catch (NumberFormatException e) {
1059: return null;
1060: }
1061: argumentsString = argumentsString.substring(index + 1, length);
1062:
1063: String[] args = new String[length];
1064: int count = 0;
1065:
1066: StringTokenizer tokenizer = new StringTokenizer(
1067: argumentsString, ARGUMENTS_DELIMITER);
1068: while (tokenizer.hasMoreTokens()) {
1069: String argument = tokenizer.nextToken();
1070: if (argument.equals(EMPTY_ARGUMENT))
1071: argument = ""; //$NON-NLS-1$
1072: args[count++] = argument;
1073: }
1074:
1075: if (count != numberOfArg)
1076: return null;
1077:
1078: System.arraycopy(args, 0, args = new String[count], 0, count);
1079: return args;
1080: }
1081:
1082: /**
1083: * Returns the given file's contents as a byte array.
1084: */
1085: public static byte[] getResourceContentsAsByteArray(IFile file)
1086: throws JavaModelException {
1087: InputStream stream = null;
1088: try {
1089: stream = file.getContents(true);
1090: } catch (CoreException e) {
1091: throw new JavaModelException(e);
1092: }
1093: try {
1094: return org.eclipse.jdt.internal.compiler.util.Util
1095: .getInputStreamAsByteArray(stream, -1);
1096: } catch (IOException e) {
1097: throw new JavaModelException(e,
1098: IJavaModelStatusConstants.IO_EXCEPTION);
1099: } finally {
1100: try {
1101: stream.close();
1102: } catch (IOException e) {
1103: // ignore
1104: }
1105: }
1106: }
1107:
1108: /**
1109: * Returns the given file's contents as a character array.
1110: */
1111: public static char[] getResourceContentsAsCharArray(IFile file)
1112: throws JavaModelException {
1113: // Get encoding from file
1114: String encoding;
1115: try {
1116: encoding = file.getCharset();
1117: } catch (CoreException ce) {
1118: // do not use any encoding
1119: encoding = null;
1120: }
1121: return getResourceContentsAsCharArray(file, encoding);
1122: }
1123:
1124: public static char[] getResourceContentsAsCharArray(IFile file,
1125: String encoding) throws JavaModelException {
1126: // Get file length
1127: // workaround https://bugs.eclipse.org/bugs/show_bug.cgi?id=130736 by using java.io.File if possible
1128: IPath location = file.getLocation();
1129: long length;
1130: if (location == null) {
1131: // non local file
1132: try {
1133: length = EFS.getStore(file.getLocationURI())
1134: .fetchInfo().getLength();
1135: } catch (CoreException e) {
1136: throw new JavaModelException(e);
1137: }
1138: } else {
1139: // local file
1140: length = location.toFile().length();
1141: }
1142:
1143: // Get resource contents
1144: InputStream stream = null;
1145: try {
1146: stream = file.getContents(true);
1147: } catch (CoreException e) {
1148: throw new JavaModelException(e,
1149: IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST);
1150: }
1151: try {
1152: return org.eclipse.jdt.internal.compiler.util.Util
1153: .getInputStreamAsCharArray(stream, (int) length,
1154: encoding);
1155: } catch (IOException e) {
1156: throw new JavaModelException(e,
1157: IJavaModelStatusConstants.IO_EXCEPTION);
1158: } finally {
1159: try {
1160: stream.close();
1161: } catch (IOException e) {
1162: // ignore
1163: }
1164: }
1165: }
1166:
1167: /*
1168: * Returns the signature of the given type.
1169: */
1170: public static String getSignature(Type type) {
1171: StringBuffer buffer = new StringBuffer();
1172: getFullyQualifiedName(type, buffer);
1173: return Signature
1174: .createTypeSignature(buffer.toString(), false/*not resolved in source*/);
1175: }
1176:
1177: /*
1178: * Returns the source attachment property for this package fragment root's path
1179: */
1180: public static String getSourceAttachmentProperty(IPath path)
1181: throws JavaModelException {
1182: Map rootPathToAttachments = JavaModelManager
1183: .getJavaModelManager().rootPathToAttachments;
1184: String property = (String) rootPathToAttachments.get(path);
1185: if (property == null) {
1186: try {
1187: property = ResourcesPlugin.getWorkspace().getRoot()
1188: .getPersistentProperty(
1189: getSourceAttachmentPropertyName(path));
1190: if (property == null) {
1191: rootPathToAttachments.put(path,
1192: PackageFragmentRoot.NO_SOURCE_ATTACHMENT);
1193: return null;
1194: }
1195: rootPathToAttachments.put(path, property);
1196: return property;
1197: } catch (CoreException e) {
1198: throw new JavaModelException(e);
1199: }
1200: } else if (property
1201: .equals(PackageFragmentRoot.NO_SOURCE_ATTACHMENT)) {
1202: return null;
1203: } else
1204: return property;
1205: }
1206:
1207: private static QualifiedName getSourceAttachmentPropertyName(
1208: IPath path) {
1209: return new QualifiedName(JavaCore.PLUGIN_ID,
1210: "sourceattachment: " + path.toOSString()); //$NON-NLS-1$
1211: }
1212:
1213: public static void setSourceAttachmentProperty(IPath path,
1214: String property) {
1215: JavaModelManager.getJavaModelManager().rootPathToAttachments
1216: .put(path, property);
1217: try {
1218: ResourcesPlugin.getWorkspace().getRoot()
1219: .setPersistentProperty(
1220: getSourceAttachmentPropertyName(path),
1221: property);
1222: } catch (CoreException e) {
1223: e.printStackTrace();
1224: }
1225: }
1226:
1227: /*
1228: * Returns the declaring type signature of the element represented by the given binding key.
1229: * Returns the signature of the element if it is a type.
1230: *
1231: * @return the declaring type signature
1232: */
1233: public static String getDeclaringTypeSignature(String key) {
1234: KeyToSignature keyToSignature = new KeyToSignature(key,
1235: KeyToSignature.DECLARING_TYPE);
1236: keyToSignature.parse();
1237: return keyToSignature.signature.toString();
1238: }
1239:
1240: /*
1241: * Appends to the given buffer the fully qualified name (as it appears in the source) of the given type
1242: */
1243: private static void getFullyQualifiedName(Type type,
1244: StringBuffer buffer) {
1245: switch (type.getNodeType()) {
1246: case ASTNode.ARRAY_TYPE:
1247: ArrayType arrayType = (ArrayType) type;
1248: getFullyQualifiedName(arrayType.getElementType(), buffer);
1249: for (int i = 0, length = arrayType.getDimensions(); i < length; i++) {
1250: buffer.append('[');
1251: buffer.append(']');
1252: }
1253: break;
1254: case ASTNode.PARAMETERIZED_TYPE:
1255: ParameterizedType parameterizedType = (ParameterizedType) type;
1256: getFullyQualifiedName(parameterizedType.getType(), buffer);
1257: buffer.append('<');
1258: Iterator iterator = parameterizedType.typeArguments()
1259: .iterator();
1260: boolean isFirst = true;
1261: while (iterator.hasNext()) {
1262: if (!isFirst)
1263: buffer.append(',');
1264: else
1265: isFirst = false;
1266: Type typeArgument = (Type) iterator.next();
1267: getFullyQualifiedName(typeArgument, buffer);
1268: }
1269: buffer.append('>');
1270: break;
1271: case ASTNode.PRIMITIVE_TYPE:
1272: buffer.append(((PrimitiveType) type).getPrimitiveTypeCode()
1273: .toString());
1274: break;
1275: case ASTNode.QUALIFIED_TYPE:
1276: buffer.append(((QualifiedType) type).getName()
1277: .getFullyQualifiedName());
1278: break;
1279: case ASTNode.SIMPLE_TYPE:
1280: buffer.append(((SimpleType) type).getName()
1281: .getFullyQualifiedName());
1282: break;
1283: case ASTNode.WILDCARD_TYPE:
1284: buffer.append('?');
1285: WildcardType wildcardType = (WildcardType) type;
1286: Type bound = wildcardType.getBound();
1287: if (bound == null)
1288: return;
1289: if (wildcardType.isUpperBound()) {
1290: buffer.append(" extends "); //$NON-NLS-1$
1291: } else {
1292: buffer.append(" super "); //$NON-NLS-1$
1293: }
1294: getFullyQualifiedName(bound, buffer);
1295: break;
1296: }
1297: }
1298:
1299: /**
1300: * Returns a trimmed version the simples names returned by Signature.
1301: */
1302: public static String[] getTrimmedSimpleNames(String name) {
1303: String[] result = Signature.getSimpleNames(name);
1304: for (int i = 0, length = result.length; i < length; i++) {
1305: result[i] = result[i].trim();
1306: }
1307: return result;
1308: }
1309:
1310: /*
1311: * Returns the index of the most specific argument paths which is strictly enclosing the path to check
1312: */
1313: public static int indexOfEnclosingPath(IPath checkedPath,
1314: IPath[] paths, int pathCount) {
1315:
1316: int bestMatch = -1, bestLength = -1;
1317: for (int i = 0; i < pathCount; i++) {
1318: if (paths[i].equals(checkedPath))
1319: continue;
1320: if (paths[i].isPrefixOf(checkedPath)) {
1321: int currentLength = paths[i].segmentCount();
1322: if (currentLength > bestLength) {
1323: bestLength = currentLength;
1324: bestMatch = i;
1325: }
1326: }
1327: }
1328: return bestMatch;
1329: }
1330:
1331: /*
1332: * Returns the index of the Java like extension of the given file name
1333: * or -1 if it doesn't end with a known Java like extension.
1334: * Note this is the index of the '.' even if it is not considered part of the extension.
1335: */
1336: public static int indexOfJavaLikeExtension(String fileName) {
1337: int fileNameLength = fileName.length();
1338: char[][] javaLikeExtensions = getJavaLikeExtensions();
1339: extensions: for (int i = 0, length = javaLikeExtensions.length; i < length; i++) {
1340: char[] extension = javaLikeExtensions[i];
1341: int extensionLength = extension.length;
1342: int extensionStart = fileNameLength - extensionLength;
1343: int dotIndex = extensionStart - 1;
1344: if (dotIndex < 0)
1345: continue;
1346: if (fileName.charAt(dotIndex) != '.')
1347: continue;
1348: for (int j = 0; j < extensionLength; j++) {
1349: if (fileName.charAt(extensionStart + j) != extension[j])
1350: continue extensions;
1351: }
1352: return dotIndex;
1353: }
1354: return -1;
1355: }
1356:
1357: /*
1358: * Returns the index of the first argument paths which is equal to the path to check
1359: */
1360: public static int indexOfMatchingPath(IPath checkedPath,
1361: IPath[] paths, int pathCount) {
1362:
1363: for (int i = 0; i < pathCount; i++) {
1364: if (paths[i].equals(checkedPath))
1365: return i;
1366: }
1367: return -1;
1368: }
1369:
1370: /*
1371: * Returns the index of the first argument paths which is strictly nested inside the path to check
1372: */
1373: public static int indexOfNestedPath(IPath checkedPath,
1374: IPath[] paths, int pathCount) {
1375:
1376: for (int i = 0; i < pathCount; i++) {
1377: if (checkedPath.equals(paths[i]))
1378: continue;
1379: if (checkedPath.isPrefixOf(paths[i]))
1380: return i;
1381: }
1382: return -1;
1383: }
1384:
1385: /**
1386: * Returns whether the local file system supports accessing and modifying
1387: * the given attribute.
1388: */
1389: protected static boolean isAttributeSupported(int attribute) {
1390: return (EFS.getLocalFileSystem().attributes() & attribute) != 0;
1391: }
1392:
1393: /**
1394: * Returns whether the given resource is read-only or not.
1395: * @param resource
1396: * @return <code>true</code> if the resource is read-only, <code>false</code> if it is not or
1397: * if the file system does not support the read-only attribute.
1398: */
1399: public static boolean isReadOnly(IResource resource) {
1400: if (isReadOnlySupported()) {
1401: ResourceAttributes resourceAttributes = resource
1402: .getResourceAttributes();
1403: if (resourceAttributes == null)
1404: return false; // not supported on this platform for this resource
1405: return resourceAttributes.isReadOnly();
1406: }
1407: return false;
1408: }
1409:
1410: /**
1411: * Returns whether the local file system supports accessing and modifying
1412: * the read only flag.
1413: */
1414: public static boolean isReadOnlySupported() {
1415: return isAttributeSupported(EFS.ATTRIBUTE_READ_ONLY);
1416: }
1417:
1418: /*
1419: * Returns whether the given java element is exluded from its root's classpath.
1420: * It doesn't check whether the root itself is on the classpath or not
1421: */
1422: public static final boolean isExcluded(IJavaElement element) {
1423: int elementType = element.getElementType();
1424: switch (elementType) {
1425: case IJavaElement.JAVA_MODEL:
1426: case IJavaElement.JAVA_PROJECT:
1427: case IJavaElement.PACKAGE_FRAGMENT_ROOT:
1428: return false;
1429:
1430: case IJavaElement.PACKAGE_FRAGMENT:
1431: PackageFragmentRoot root = (PackageFragmentRoot) element
1432: .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
1433: IResource resource = element.getResource();
1434: return resource != null
1435: && isExcluded(resource, root
1436: .fullInclusionPatternChars(), root
1437: .fullExclusionPatternChars());
1438:
1439: case IJavaElement.COMPILATION_UNIT:
1440: root = (PackageFragmentRoot) element
1441: .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
1442: resource = element.getResource();
1443: if (resource == null)
1444: return false;
1445: if (isExcluded(resource, root.fullInclusionPatternChars(),
1446: root.fullExclusionPatternChars()))
1447: return true;
1448: return isExcluded(element.getParent());
1449:
1450: default:
1451: IJavaElement cu = element
1452: .getAncestor(IJavaElement.COMPILATION_UNIT);
1453: return cu != null && isExcluded(cu);
1454: }
1455: }
1456:
1457: /*
1458: * Returns whether the given resource path matches one of the inclusion/exclusion
1459: * patterns.
1460: * NOTE: should not be asked directly using pkg root pathes
1461: * @see IClasspathEntry#getInclusionPatterns
1462: * @see IClasspathEntry#getExclusionPatterns
1463: */
1464: public final static boolean isExcluded(IPath resourcePath,
1465: char[][] inclusionPatterns, char[][] exclusionPatterns,
1466: boolean isFolderPath) {
1467: if (inclusionPatterns == null && exclusionPatterns == null)
1468: return false;
1469: return org.eclipse.jdt.internal.compiler.util.Util.isExcluded(
1470: resourcePath.toString().toCharArray(),
1471: inclusionPatterns, exclusionPatterns, isFolderPath);
1472: }
1473:
1474: /*
1475: * Returns whether the given resource matches one of the exclusion patterns.
1476: * NOTE: should not be asked directly using pkg root pathes
1477: * @see IClasspathEntry#getExclusionPatterns
1478: */
1479: public final static boolean isExcluded(IResource resource,
1480: char[][] inclusionPatterns, char[][] exclusionPatterns) {
1481: IPath path = resource.getFullPath();
1482: // ensure that folders are only excluded if all of their children are excluded
1483: int resourceType = resource.getType();
1484: return isExcluded(path, inclusionPatterns, exclusionPatterns,
1485: resourceType == IResource.FOLDER
1486: || resourceType == IResource.PROJECT);
1487: }
1488:
1489: /**
1490: * Validate the given .class file name.
1491: * A .class file name must obey the following rules:
1492: * <ul>
1493: * <li> it must not be null
1494: * <li> it must include the <code>".class"</code> suffix
1495: * <li> its prefix must be a valid identifier
1496: * </ul>
1497: * </p>
1498: * @param name the name of a .class file
1499: * @param sourceLevel the source level
1500: * @param complianceLevel the compliance level
1501: * @return a status object with code <code>IStatus.OK</code> if
1502: * the given name is valid as a .class file name, otherwise a status
1503: * object indicating what is wrong with the name
1504: */
1505: public static boolean isValidClassFileName(String name,
1506: String sourceLevel, String complianceLevel) {
1507: return JavaConventions.validateClassFileName(name, sourceLevel,
1508: complianceLevel).getSeverity() != IStatus.ERROR;
1509: }
1510:
1511: /**
1512: * Validate the given compilation unit name.
1513: * A compilation unit name must obey the following rules:
1514: * <ul>
1515: * <li> it must not be null
1516: * <li> it must include the <code>".java"</code> suffix
1517: * <li> its prefix must be a valid identifier
1518: * </ul>
1519: * </p>
1520: * @param name the name of a compilation unit
1521: * @param sourceLevel the source level
1522: * @param complianceLevel the compliance level
1523: * @return a status object with code <code>IStatus.OK</code> if
1524: * the given name is valid as a compilation unit name, otherwise a status
1525: * object indicating what is wrong with the name
1526: */
1527: public static boolean isValidCompilationUnitName(String name,
1528: String sourceLevel, String complianceLevel) {
1529: return JavaConventions.validateCompilationUnitName(name,
1530: sourceLevel, complianceLevel).getSeverity() != IStatus.ERROR;
1531: }
1532:
1533: /**
1534: * Returns true if the given folder name is valid for a package,
1535: * false if it is not.
1536: * @param folderName the name of the folder
1537: * @param sourceLevel the source level
1538: * @param complianceLevel the compliance level
1539: */
1540: public static boolean isValidFolderNameForPackage(
1541: String folderName, String sourceLevel,
1542: String complianceLevel) {
1543: return JavaConventions.validateIdentifier(folderName,
1544: sourceLevel, complianceLevel).getSeverity() != IStatus.ERROR;
1545: }
1546:
1547: /**
1548: * Returns true if the given method signature is valid,
1549: * false if it is not.
1550: */
1551: public static boolean isValidMethodSignature(String sig) {
1552: int len = sig.length();
1553: if (len == 0)
1554: return false;
1555: int i = 0;
1556: char c = sig.charAt(i++);
1557: if (c != '(')
1558: return false;
1559: if (i >= len)
1560: return false;
1561: while (sig.charAt(i) != ')') {
1562: // Void is not allowed as a parameter type.
1563: i = checkTypeSignature(sig, i, len, false);
1564: if (i == -1)
1565: return false;
1566: if (i >= len)
1567: return false;
1568: }
1569: ++i;
1570: i = checkTypeSignature(sig, i, len, true);
1571: return i == len;
1572: }
1573:
1574: /**
1575: * Returns true if the given type signature is valid,
1576: * false if it is not.
1577: */
1578: public static boolean isValidTypeSignature(String sig,
1579: boolean allowVoid) {
1580: int len = sig.length();
1581: return checkTypeSignature(sig, 0, len, allowVoid) == len;
1582: }
1583:
1584: /*
1585: * Returns the simple name of a local type from the given binary type name.
1586: * The last '$' is at lastDollar. The last character of the type name is at end-1.
1587: */
1588: public static String localTypeName(String binaryTypeName,
1589: int lastDollar, int end) {
1590: if (lastDollar > 0
1591: && binaryTypeName.charAt(lastDollar - 1) == '$')
1592: // local name starts with a dollar sign
1593: // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=103466)
1594: return binaryTypeName;
1595: int nameStart = lastDollar + 1;
1596: while (nameStart < end
1597: && Character.isDigit(binaryTypeName.charAt(nameStart)))
1598: nameStart++;
1599: return binaryTypeName.substring(nameStart, end);
1600: }
1601:
1602: /*
1603: * Add a log entry
1604: */
1605: public static void log(Throwable e, String message) {
1606: Throwable nestedException;
1607: if (e instanceof JavaModelException
1608: && (nestedException = ((JavaModelException) e)
1609: .getException()) != null) {
1610: e = nestedException;
1611: }
1612: IStatus status = new Status(IStatus.ERROR, JavaCore.PLUGIN_ID,
1613: IStatus.ERROR, message, e);
1614: JavaCore.getPlugin().getLog().log(status);
1615: }
1616:
1617: public static ClassFileReader newClassFileReader(IResource resource)
1618: throws CoreException, ClassFormatException, IOException {
1619: InputStream in = null;
1620: try {
1621: in = ((IFile) resource).getContents(true);
1622: return ClassFileReader.read(in, resource.getFullPath()
1623: .toString());
1624: } finally {
1625: if (in != null)
1626: in.close();
1627: }
1628: }
1629:
1630: /**
1631: * Normalizes the cariage returns in the given text.
1632: * They are all changed to use the given buffer's line separator.
1633: */
1634: public static char[] normalizeCRs(char[] text, char[] buffer) {
1635: CharArrayBuffer result = new CharArrayBuffer();
1636: int lineStart = 0;
1637: int length = text.length;
1638: if (length == 0)
1639: return text;
1640: String lineSeparator = getLineSeparator(text, buffer);
1641: char nextChar = text[0];
1642: for (int i = 0; i < length; i++) {
1643: char currentChar = nextChar;
1644: nextChar = i < length - 1 ? text[i + 1] : ' ';
1645: switch (currentChar) {
1646: case '\n':
1647: int lineLength = i - lineStart;
1648: char[] line = new char[lineLength];
1649: System.arraycopy(text, lineStart, line, 0, lineLength);
1650: result.append(line);
1651: result.append(lineSeparator);
1652: lineStart = i + 1;
1653: break;
1654: case '\r':
1655: lineLength = i - lineStart;
1656: if (lineLength >= 0) {
1657: line = new char[lineLength];
1658: System.arraycopy(text, lineStart, line, 0,
1659: lineLength);
1660: result.append(line);
1661: result.append(lineSeparator);
1662: if (nextChar == '\n') {
1663: nextChar = ' ';
1664: lineStart = i + 2;
1665: } else {
1666: // when line separator are mixed in the same file
1667: // \r might not be followed by a \n. If not, we should increment
1668: // lineStart by one and not by two.
1669: lineStart = i + 1;
1670: }
1671: } else {
1672: // when line separator are mixed in the same file
1673: // we need to prevent NegativeArraySizeException
1674: lineStart = i + 1;
1675: }
1676: break;
1677: }
1678: }
1679: char[] lastLine;
1680: if (lineStart > 0) {
1681: int lastLineLength = length - lineStart;
1682: if (lastLineLength > 0) {
1683: lastLine = new char[lastLineLength];
1684: System.arraycopy(text, lineStart, lastLine, 0,
1685: lastLineLength);
1686: result.append(lastLine);
1687: }
1688: return result.getContents();
1689: }
1690: return text;
1691: }
1692:
1693: /**
1694: * Normalizes the cariage returns in the given text.
1695: * They are all changed to use given buffer's line sepatator.
1696: */
1697: public static String normalizeCRs(String text, String buffer) {
1698: return new String(normalizeCRs(text.toCharArray(), buffer
1699: .toCharArray()));
1700: }
1701:
1702: /**
1703: * Converts the given relative path into a package name.
1704: * Returns null if the path is not a valid package name.
1705: * @param pkgPath the package path
1706: * @param sourceLevel the source level
1707: * @param complianceLevel the compliance level
1708: */
1709: public static String packageName(IPath pkgPath, String sourceLevel,
1710: String complianceLevel) {
1711: StringBuffer pkgName = new StringBuffer(
1712: IPackageFragment.DEFAULT_PACKAGE_NAME);
1713: for (int j = 0, max = pkgPath.segmentCount(); j < max; j++) {
1714: String segment = pkgPath.segment(j);
1715: if (!isValidFolderNameForPackage(segment, sourceLevel,
1716: complianceLevel)) {
1717: return null;
1718: }
1719: pkgName.append(segment);
1720: if (j < pkgPath.segmentCount() - 1) {
1721: pkgName.append("."); //$NON-NLS-1$
1722: }
1723: }
1724: return pkgName.toString();
1725: }
1726:
1727: /**
1728: * Returns the length of the common prefix between s1 and s2.
1729: */
1730: public static int prefixLength(char[] s1, char[] s2) {
1731: int len = 0;
1732: int max = Math.min(s1.length, s2.length);
1733: for (int i = 0; i < max && s1[i] == s2[i]; ++i)
1734: ++len;
1735: return len;
1736: }
1737:
1738: /**
1739: * Returns the length of the common prefix between s1 and s2.
1740: */
1741: public static int prefixLength(String s1, String s2) {
1742: int len = 0;
1743: int max = Math.min(s1.length(), s2.length());
1744: for (int i = 0; i < max && s1.charAt(i) == s2.charAt(i); ++i)
1745: ++len;
1746: return len;
1747: }
1748:
1749: private static void quickSort(char[][] list, int left, int right) {
1750: int original_left = left;
1751: int original_right = right;
1752: char[] mid = list[left + (right - left) / 2];
1753: do {
1754: while (compare(list[left], mid) < 0) {
1755: left++;
1756: }
1757: while (compare(mid, list[right]) < 0) {
1758: right--;
1759: }
1760: if (left <= right) {
1761: char[] tmp = list[left];
1762: list[left] = list[right];
1763: list[right] = tmp;
1764: left++;
1765: right--;
1766: }
1767: } while (left <= right);
1768: if (original_left < right) {
1769: quickSort(list, original_left, right);
1770: }
1771: if (left < original_right) {
1772: quickSort(list, left, original_right);
1773: }
1774: }
1775:
1776: /**
1777: * Sort the comparable objects in the given collection.
1778: */
1779: private static void quickSort(Comparable[] sortedCollection,
1780: int left, int right) {
1781: int original_left = left;
1782: int original_right = right;
1783: Comparable mid = sortedCollection[left + (right - left) / 2];
1784: do {
1785: while (sortedCollection[left].compareTo(mid) < 0) {
1786: left++;
1787: }
1788: while (mid.compareTo(sortedCollection[right]) < 0) {
1789: right--;
1790: }
1791: if (left <= right) {
1792: Comparable tmp = sortedCollection[left];
1793: sortedCollection[left] = sortedCollection[right];
1794: sortedCollection[right] = tmp;
1795: left++;
1796: right--;
1797: }
1798: } while (left <= right);
1799: if (original_left < right) {
1800: quickSort(sortedCollection, original_left, right);
1801: }
1802: if (left < original_right) {
1803: quickSort(sortedCollection, left, original_right);
1804: }
1805: }
1806:
1807: private static void quickSort(int[] list, int left, int right) {
1808: int original_left = left;
1809: int original_right = right;
1810: int mid = list[left + (right - left) / 2];
1811: do {
1812: while (list[left] < mid) {
1813: left++;
1814: }
1815: while (mid < list[right]) {
1816: right--;
1817: }
1818: if (left <= right) {
1819: int tmp = list[left];
1820: list[left] = list[right];
1821: list[right] = tmp;
1822: left++;
1823: right--;
1824: }
1825: } while (left <= right);
1826: if (original_left < right) {
1827: quickSort(list, original_left, right);
1828: }
1829: if (left < original_right) {
1830: quickSort(list, left, original_right);
1831: }
1832: }
1833:
1834: /**
1835: * Sort the objects in the given collection using the given comparer.
1836: */
1837: private static void quickSort(Object[] sortedCollection, int left,
1838: int right, Comparer comparer) {
1839: int original_left = left;
1840: int original_right = right;
1841: Object mid = sortedCollection[left + (right - left) / 2];
1842: do {
1843: while (comparer.compare(sortedCollection[left], mid) < 0) {
1844: left++;
1845: }
1846: while (comparer.compare(mid, sortedCollection[right]) < 0) {
1847: right--;
1848: }
1849: if (left <= right) {
1850: Object tmp = sortedCollection[left];
1851: sortedCollection[left] = sortedCollection[right];
1852: sortedCollection[right] = tmp;
1853: left++;
1854: right--;
1855: }
1856: } while (left <= right);
1857: if (original_left < right) {
1858: quickSort(sortedCollection, original_left, right, comparer);
1859: }
1860: if (left < original_right) {
1861: quickSort(sortedCollection, left, original_right, comparer);
1862: }
1863: }
1864:
1865: /**
1866: * Sort the strings in the given collection.
1867: */
1868: private static void quickSort(String[] sortedCollection, int left,
1869: int right) {
1870: int original_left = left;
1871: int original_right = right;
1872: String mid = sortedCollection[left + (right - left) / 2];
1873: do {
1874: while (sortedCollection[left].compareTo(mid) < 0) {
1875: left++;
1876: }
1877: while (mid.compareTo(sortedCollection[right]) < 0) {
1878: right--;
1879: }
1880: if (left <= right) {
1881: String tmp = sortedCollection[left];
1882: sortedCollection[left] = sortedCollection[right];
1883: sortedCollection[right] = tmp;
1884: left++;
1885: right--;
1886: }
1887: } while (left <= right);
1888: if (original_left < right) {
1889: quickSort(sortedCollection, original_left, right);
1890: }
1891: if (left < original_right) {
1892: quickSort(sortedCollection, left, original_right);
1893: }
1894: }
1895:
1896: /**
1897: * Returns the toString() of the given full path minus the first given number of segments.
1898: * The returned string is always a relative path (it has no leading slash)
1899: */
1900: public static String relativePath(IPath fullPath,
1901: int skipSegmentCount) {
1902: boolean hasTrailingSeparator = fullPath.hasTrailingSeparator();
1903: String[] segments = fullPath.segments();
1904:
1905: // compute length
1906: int length = 0;
1907: int max = segments.length;
1908: if (max > skipSegmentCount) {
1909: for (int i1 = skipSegmentCount; i1 < max; i1++) {
1910: length += segments[i1].length();
1911: }
1912: //add the separator lengths
1913: length += max - skipSegmentCount - 1;
1914: }
1915: if (hasTrailingSeparator)
1916: length++;
1917:
1918: char[] result = new char[length];
1919: int offset = 0;
1920: int len = segments.length - 1;
1921: if (len >= skipSegmentCount) {
1922: //append all but the last segment, with separators
1923: for (int i = skipSegmentCount; i < len; i++) {
1924: int size = segments[i].length();
1925: segments[i].getChars(0, size, result, offset);
1926: offset += size;
1927: result[offset++] = '/';
1928: }
1929: //append the last segment
1930: int size = segments[len].length();
1931: segments[len].getChars(0, size, result, offset);
1932: offset += size;
1933: }
1934: if (hasTrailingSeparator)
1935: result[offset++] = '/';
1936: return new String(result);
1937: }
1938:
1939: /*
1940: * Resets the list of Java-like extensions after a change in content-type.
1941: */
1942: public static void resetJavaLikeExtensions() {
1943: JAVA_LIKE_EXTENSIONS = null;
1944: }
1945:
1946: /**
1947: * Return a new array which is the split of the given string using the given divider. The given end
1948: * is exclusive and the given start is inclusive.
1949: * <br>
1950: * <br>
1951: * For example:
1952: * <ol>
1953: * <li><pre>
1954: * divider = 'b'
1955: * string = "abbaba"
1956: * start = 2
1957: * end = 5
1958: * result => { "", "a", "" }
1959: * </pre>
1960: * </li>
1961: * </ol>
1962: *
1963: * @param divider the given divider
1964: * @param string the given string
1965: * @param start the given starting index
1966: * @param end the given ending index
1967: * @return a new array which is the split of the given string using the given divider
1968: * @throws ArrayIndexOutOfBoundsException if start is lower than 0 or end is greater than the array length
1969: */
1970: public static final String[] splitOn(char divider, String string,
1971: int start, int end) {
1972: int length = string == null ? 0 : string.length();
1973: if (length == 0 || start > end)
1974: return CharOperation.NO_STRINGS;
1975:
1976: int wordCount = 1;
1977: for (int i = start; i < end; i++)
1978: if (string.charAt(i) == divider)
1979: wordCount++;
1980: String[] split = new String[wordCount];
1981: int last = start, currentWord = 0;
1982: for (int i = start; i < end; i++) {
1983: if (string.charAt(i) == divider) {
1984: split[currentWord++] = string.substring(last, i);
1985: last = i + 1;
1986: }
1987: }
1988: split[currentWord] = string.substring(last, end);
1989: return split;
1990: }
1991:
1992: /**
1993: * Sets or unsets the given resource as read-only in the file system.
1994: * It's a no-op if the file system does not support the read-only attribute.
1995: *
1996: * @param resource The resource to set as read-only
1997: * @param readOnly <code>true</code> to set it to read-only,
1998: * <code>false</code> to unset
1999: */
2000: public static void setReadOnly(IResource resource, boolean readOnly) {
2001: if (isReadOnlySupported()) {
2002: ResourceAttributes resourceAttributes = resource
2003: .getResourceAttributes();
2004: if (resourceAttributes == null)
2005: return; // not supported on this platform for this resource
2006: resourceAttributes.setReadOnly(readOnly);
2007: try {
2008: resource.setResourceAttributes(resourceAttributes);
2009: } catch (CoreException e) {
2010: // ignore
2011: }
2012: }
2013: }
2014:
2015: public static void sort(char[][] list) {
2016: if (list.length > 1)
2017: quickSort(list, 0, list.length - 1);
2018: }
2019:
2020: /**
2021: * Sorts an array of Comparable objects in place.
2022: */
2023: public static void sort(Comparable[] objects) {
2024: if (objects.length > 1)
2025: quickSort(objects, 0, objects.length - 1);
2026: }
2027:
2028: public static void sort(int[] list) {
2029: if (list.length > 1)
2030: quickSort(list, 0, list.length - 1);
2031: }
2032:
2033: /**
2034: * Sorts an array of objects in place.
2035: * The given comparer compares pairs of items.
2036: */
2037: public static void sort(Object[] objects, Comparer comparer) {
2038: if (objects.length > 1)
2039: quickSort(objects, 0, objects.length - 1, comparer);
2040: }
2041:
2042: /**
2043: * Sorts an array of strings in place using quicksort.
2044: */
2045: public static void sort(String[] strings) {
2046: if (strings.length > 1)
2047: quickSort(strings, 0, strings.length - 1);
2048: }
2049:
2050: /**
2051: * Sorts an array of Comparable objects, returning a new array
2052: * with the sorted items. The original array is left untouched.
2053: */
2054: public static Comparable[] sortCopy(Comparable[] objects) {
2055: int len = objects.length;
2056: Comparable[] copy = new Comparable[len];
2057: System.arraycopy(objects, 0, copy, 0, len);
2058: sort(copy);
2059: return copy;
2060: }
2061:
2062: /**
2063: * Sorts an array of Java elements based on their toStringWithAncestors(),
2064: * returning a new array with the sorted items.
2065: * The original array is left untouched.
2066: */
2067: public static IJavaElement[] sortCopy(IJavaElement[] elements) {
2068: int len = elements.length;
2069: IJavaElement[] copy = new IJavaElement[len];
2070: System.arraycopy(elements, 0, copy, 0, len);
2071: sort(copy, new Comparer() {
2072: public int compare(Object a, Object b) {
2073: return ((JavaElement) a).toStringWithAncestors()
2074: .compareTo(
2075: ((JavaElement) b)
2076: .toStringWithAncestors());
2077: }
2078: });
2079: return copy;
2080: }
2081:
2082: /**
2083: * Sorts an array of Strings, returning a new array
2084: * with the sorted items. The original array is left untouched.
2085: */
2086: public static Object[] sortCopy(Object[] objects, Comparer comparer) {
2087: int len = objects.length;
2088: Object[] copy = new Object[len];
2089: System.arraycopy(objects, 0, copy, 0, len);
2090: sort(copy, comparer);
2091: return copy;
2092: }
2093:
2094: /**
2095: * Sorts an array of Strings, returning a new array
2096: * with the sorted items. The original array is left untouched.
2097: */
2098: public static String[] sortCopy(String[] objects) {
2099: int len = objects.length;
2100: String[] copy = new String[len];
2101: System.arraycopy(objects, 0, copy, 0, len);
2102: sort(copy);
2103: return copy;
2104: }
2105:
2106: /*
2107: * Returns whether the given compound name starts with the given prefix.
2108: * Returns true if the n first elements of the prefix are equals and the last element of the
2109: * prefix is a prefix of the corresponding element in the compound name.
2110: */
2111: public static boolean startsWithIgnoreCase(String[] compoundName,
2112: String[] prefix, boolean partialMatch) {
2113: int prefixLength = prefix.length;
2114: int nameLength = compoundName.length;
2115: if (prefixLength > nameLength)
2116: return false;
2117: for (int i = 0; i < prefixLength - 1; i++) {
2118: if (!compoundName[i].equalsIgnoreCase(prefix[i]))
2119: return false;
2120: }
2121: return (partialMatch || prefixLength == nameLength)
2122: && compoundName[prefixLength - 1].toLowerCase()
2123: .startsWith(
2124: prefix[prefixLength - 1].toLowerCase());
2125: }
2126:
2127: /*
2128: * Returns whether the given compound name matches the given pattern.
2129: */
2130: public static boolean matchesWithIgnoreCase(String[] compoundName,
2131: String pattern) {
2132: if (pattern.equals("*"))return true; //$NON-NLS-1$
2133: int nameLength = compoundName.length;
2134: if (pattern.length() == 0)
2135: return nameLength == 0;
2136: if (nameLength == 0)
2137: return false;
2138: int length = nameLength - 1;
2139: for (int i = 0; i < nameLength; i++) {
2140: length += compoundName[i].length();
2141: }
2142: char[] compoundChars = new char[length];
2143: int pos = 0;
2144: for (int i = 0; i < nameLength; i++) {
2145: if (pos > 0)
2146: compoundChars[pos++] = '.';
2147: char[] array = compoundName[i].toCharArray();
2148: int size = array.length;
2149: System.arraycopy(array, 0, compoundChars, pos, size);
2150: pos += size;
2151: }
2152: return CharOperation.match(pattern.toCharArray(),
2153: compoundChars, false);
2154: }
2155:
2156: /**
2157: * Converts a String[] to char[][].
2158: */
2159: public static char[][] toCharArrays(String[] a) {
2160: int len = a.length;
2161: if (len == 0)
2162: return CharOperation.NO_CHAR_CHAR;
2163: char[][] result = new char[len][];
2164: for (int i = 0; i < len; ++i) {
2165: result[i] = a[i].toCharArray();
2166: }
2167: return result;
2168: }
2169:
2170: /**
2171: * Converts a String to char[][], where segments are separate by '.'.
2172: */
2173: public static char[][] toCompoundChars(String s) {
2174: int len = s.length();
2175: if (len == 0) {
2176: return CharOperation.NO_CHAR_CHAR;
2177: }
2178: int segCount = 1;
2179: for (int off = s.indexOf('.'); off != -1; off = s.indexOf('.',
2180: off + 1)) {
2181: ++segCount;
2182: }
2183: char[][] segs = new char[segCount][];
2184: int start = 0;
2185: for (int i = 0; i < segCount; ++i) {
2186: int dot = s.indexOf('.', start);
2187: int end = (dot == -1 ? s.length() : dot);
2188: segs[i] = new char[end - start];
2189: s.getChars(start, end, segs[i], 0);
2190: start = end + 1;
2191: }
2192: return segs;
2193: }
2194:
2195: /*
2196: * Converts the given URI to a local file. Use the existing file if the uri is on the local file system.
2197: * Otherwise fetch it.
2198: * Returns null if unable to fetch it.
2199: */
2200: public static File toLocalFile(URI uri, IProgressMonitor monitor)
2201: throws CoreException {
2202: IFileStore fileStore = EFS.getStore(uri);
2203: File localFile = fileStore.toLocalFile(EFS.NONE, monitor);
2204: if (localFile == null)
2205: // non local file system
2206: localFile = fileStore.toLocalFile(EFS.CACHE, monitor);
2207: return localFile;
2208: }
2209:
2210: /**
2211: * Converts a char[][] to String, where segments are separated by '.'.
2212: */
2213: public static String toString(char[][] c) {
2214: StringBuffer sb = new StringBuffer();
2215: for (int i = 0, max = c.length; i < max; ++i) {
2216: if (i != 0)
2217: sb.append('.');
2218: sb.append(c[i]);
2219: }
2220: return sb.toString();
2221: }
2222:
2223: /**
2224: * Converts a char[][] and a char[] to String, where segments are separated by '.'.
2225: */
2226: public static String toString(char[][] c, char[] d) {
2227: if (c == null)
2228: return new String(d);
2229: StringBuffer sb = new StringBuffer();
2230: for (int i = 0, max = c.length; i < max; ++i) {
2231: sb.append(c[i]);
2232: sb.append('.');
2233: }
2234: sb.append(d);
2235: return sb.toString();
2236: }
2237:
2238: /*
2239: * Converts a char[][] to String[].
2240: */
2241: public static String[] toStrings(char[][] a) {
2242: int len = a.length;
2243: String[] result = new String[len];
2244: for (int i = 0; i < len; ++i) {
2245: result[i] = new String(a[i]);
2246: }
2247: return result;
2248: }
2249:
2250: private static void appendArrayTypeSignature(char[] string,
2251: int start, StringBuffer buffer, boolean compact) {
2252: int length = string.length;
2253: // need a minimum 2 char
2254: if (start >= length - 1) {
2255: throw new IllegalArgumentException();
2256: }
2257: char c = string[start];
2258: if (c != Signature.C_ARRAY) {
2259: throw new IllegalArgumentException();
2260: }
2261:
2262: int index = start;
2263: c = string[++index];
2264: while (c == Signature.C_ARRAY) {
2265: // need a minimum 2 char
2266: if (index >= length - 1) {
2267: throw new IllegalArgumentException();
2268: }
2269: c = string[++index];
2270: }
2271:
2272: appendTypeSignature(string, index, buffer, compact);
2273:
2274: for (int i = 0, dims = index - start; i < dims; i++) {
2275: buffer.append('[').append(']');
2276: }
2277: }
2278:
2279: private static void appendClassTypeSignature(char[] string,
2280: int start, StringBuffer buffer, boolean compact) {
2281: char c = string[start];
2282: if (c != Signature.C_RESOLVED) {
2283: return;
2284: }
2285: int p = start + 1;
2286: int checkpoint = buffer.length();
2287: while (true) {
2288: c = string[p];
2289: switch (c) {
2290: case Signature.C_SEMICOLON:
2291: // all done
2292: return;
2293: case Signature.C_DOT:
2294: case '/':
2295: // erase package prefix
2296: if (compact) {
2297: buffer.setLength(checkpoint);
2298: } else {
2299: buffer.append('.');
2300: }
2301: break;
2302: case Signature.C_DOLLAR:
2303: /**
2304: * Convert '$' in resolved type signatures into '.'.
2305: * NOTE: This assumes that the type signature is an inner type
2306: * signature. This is true in most cases, but someone can define a
2307: * non-inner type name containing a '$'.
2308: */
2309: buffer.append('.');
2310: break;
2311: default:
2312: buffer.append(c);
2313: }
2314: p++;
2315: }
2316: }
2317:
2318: static void appendTypeSignature(char[] string, int start,
2319: StringBuffer buffer, boolean compact) {
2320: char c = string[start];
2321: switch (c) {
2322: case Signature.C_ARRAY:
2323: appendArrayTypeSignature(string, start, buffer, compact);
2324: break;
2325: case Signature.C_RESOLVED:
2326: appendClassTypeSignature(string, start, buffer, compact);
2327: break;
2328: case Signature.C_TYPE_VARIABLE:
2329: int e = Util.scanTypeVariableSignature(string, start);
2330: buffer.append(string, start + 1, e - start - 1);
2331: break;
2332: case Signature.C_BOOLEAN:
2333: buffer.append(BOOLEAN);
2334: break;
2335: case Signature.C_BYTE:
2336: buffer.append(BYTE);
2337: break;
2338: case Signature.C_CHAR:
2339: buffer.append(CHAR);
2340: break;
2341: case Signature.C_DOUBLE:
2342: buffer.append(DOUBLE);
2343: break;
2344: case Signature.C_FLOAT:
2345: buffer.append(FLOAT);
2346: break;
2347: case Signature.C_INT:
2348: buffer.append(INT);
2349: break;
2350: case Signature.C_LONG:
2351: buffer.append(LONG);
2352: break;
2353: case Signature.C_SHORT:
2354: buffer.append(SHORT);
2355: break;
2356: case Signature.C_VOID:
2357: buffer.append(VOID);
2358: break;
2359: }
2360: }
2361:
2362: public static String toString(char[] declaringClass,
2363: char[] methodName, char[] methodSignature,
2364: boolean includeReturnType, boolean compact) {
2365: final boolean isConstructor = CharOperation.equals(methodName,
2366: INIT);
2367: int firstParen = CharOperation.indexOf(Signature.C_PARAM_START,
2368: methodSignature);
2369: if (firstParen == -1) {
2370: return ""; //$NON-NLS-1$
2371: }
2372:
2373: StringBuffer buffer = new StringBuffer(
2374: methodSignature.length + 10);
2375:
2376: // decode declaring class name
2377: // it can be either an array signature or a type signature
2378: if (declaringClass.length > 0) {
2379: char[] declaringClassSignature = null;
2380: if (declaringClass[0] == Signature.C_ARRAY) {
2381: CharOperation.replace(declaringClass, '/', '.');
2382: declaringClassSignature = Signature
2383: .toCharArray(declaringClass);
2384: } else {
2385: CharOperation.replace(declaringClass, '/', '.');
2386: declaringClassSignature = declaringClass;
2387: }
2388: int lastIndexOfSlash = CharOperation.lastIndexOf('.',
2389: declaringClassSignature);
2390: if (compact && lastIndexOfSlash != -1) {
2391: buffer.append(declaringClassSignature,
2392: lastIndexOfSlash + 1,
2393: declaringClassSignature.length
2394: - lastIndexOfSlash - 1);
2395: } else {
2396: buffer.append(declaringClassSignature);
2397: }
2398: }
2399:
2400: // selector
2401: if (!isConstructor) {
2402: buffer.append('.');
2403: if (methodName != null) {
2404: buffer.append(methodName);
2405: }
2406: }
2407:
2408: // parameters
2409: buffer.append('(');
2410: char[][] pts = Signature.getParameterTypes(methodSignature);
2411: for (int i = 0, max = pts.length; i < max; i++) {
2412: appendTypeSignature(pts[i], 0, buffer, compact);
2413: if (i != pts.length - 1) {
2414: buffer.append(',');
2415: buffer.append(' ');
2416: }
2417: }
2418: buffer.append(')');
2419:
2420: if (!isConstructor) {
2421: buffer.append(" : "); //$NON-NLS-1$
2422: // return type
2423: if (includeReturnType) {
2424: char[] rts = Signature.getReturnType(methodSignature);
2425: appendTypeSignature(rts, 0, buffer, compact);
2426: }
2427: }
2428: return String.valueOf(buffer);
2429: }
2430:
2431: /*
2432: * Returns the unresolved type parameter signatures of the given method
2433: * e.g. {"QString;", "[int", "[[Qjava.util.Vector;"}
2434: */
2435: public static String[] typeParameterSignatures(
2436: AbstractMethodDeclaration method) {
2437: Argument[] args = method.arguments;
2438: if (args != null) {
2439: int length = args.length;
2440: String[] signatures = new String[length];
2441: for (int i = 0; i < args.length; i++) {
2442: Argument arg = args[i];
2443: signatures[i] = typeSignature(arg.type);
2444: }
2445: return signatures;
2446: }
2447: return CharOperation.NO_STRINGS;
2448: }
2449:
2450: /*
2451: * Returns the unresolved type signature of the given type reference,
2452: * e.g. "QString;", "[int", "[[Qjava.util.Vector;"
2453: */
2454: public static String typeSignature(TypeReference type) {
2455: char[][] compoundName = type.getParameterizedTypeName();
2456: char[] typeName = CharOperation.concatWith(compoundName, '.');
2457: String signature = Signature.createTypeSignature(typeName,
2458: false/*don't resolve*/);
2459: return signature;
2460: }
2461:
2462: /**
2463: * Asserts that the given method signature is valid.
2464: */
2465: public static void validateMethodSignature(String sig) {
2466: Assert.isTrue(isValidMethodSignature(sig));
2467: }
2468:
2469: /**
2470: * Asserts that the given type signature is valid.
2471: */
2472: public static void validateTypeSignature(String sig,
2473: boolean allowVoid) {
2474: Assert.isTrue(isValidTypeSignature(sig, allowVoid));
2475: }
2476:
2477: public static void verbose(String log) {
2478: verbose(log, System.out);
2479: }
2480:
2481: public static synchronized void verbose(String log,
2482: PrintStream printStream) {
2483: int start = 0;
2484: do {
2485: int end = log.indexOf('\n', start);
2486: printStream.print(Thread.currentThread());
2487: printStream.print(" "); //$NON-NLS-1$
2488: printStream.print(log.substring(start, end == -1 ? log
2489: .length() : end + 1));
2490: start = end + 1;
2491: } while (start != 0);
2492: printStream.println();
2493: }
2494:
2495: /**
2496: * Returns true if the given name ends with one of the known java like extension.
2497: * (implementation is not creating extra strings)
2498: */
2499: public final static boolean isJavaLikeFileName(String name) {
2500: if (name == null)
2501: return false;
2502: return indexOfJavaLikeExtension(name) != -1;
2503: }
2504:
2505: /**
2506: * Returns true if the given name ends with one of the known java like extension.
2507: * (implementation is not creating extra strings)
2508: */
2509: public final static boolean isJavaLikeFileName(char[] fileName) {
2510: if (fileName == null)
2511: return false;
2512: int fileNameLength = fileName.length;
2513: char[][] javaLikeExtensions = getJavaLikeExtensions();
2514: extensions: for (int i = 0, length = javaLikeExtensions.length; i < length; i++) {
2515: char[] extension = javaLikeExtensions[i];
2516: int extensionLength = extension.length;
2517: int extensionStart = fileNameLength - extensionLength;
2518: if (extensionStart - 1 < 0)
2519: continue;
2520: if (fileName[extensionStart - 1] != '.')
2521: continue;
2522: for (int j = 0; j < extensionLength; j++) {
2523: if (fileName[extensionStart + j] != extension[j])
2524: continue extensions;
2525: }
2526: return true;
2527: }
2528: return false;
2529: }
2530:
2531: /**
2532: * Scans the given string for a type signature starting at the given index
2533: * and returns the index of the last character.
2534: * <pre>
2535: * TypeSignature:
2536: * | BaseTypeSignature
2537: * | ArrayTypeSignature
2538: * | ClassTypeSignature
2539: * | TypeVariableSignature
2540: * </pre>
2541: *
2542: * @param string the signature string
2543: * @param start the 0-based character index of the first character
2544: * @return the 0-based character index of the last character
2545: * @exception IllegalArgumentException if this is not a type signature
2546: */
2547: public static int scanTypeSignature(char[] string, int start) {
2548: // need a minimum 1 char
2549: if (start >= string.length) {
2550: throw new IllegalArgumentException();
2551: }
2552: char c = string[start];
2553: switch (c) {
2554: case Signature.C_ARRAY:
2555: return scanArrayTypeSignature(string, start);
2556: case Signature.C_RESOLVED:
2557: case Signature.C_UNRESOLVED:
2558: return scanClassTypeSignature(string, start);
2559: case Signature.C_TYPE_VARIABLE:
2560: return scanTypeVariableSignature(string, start);
2561: case Signature.C_BOOLEAN:
2562: case Signature.C_BYTE:
2563: case Signature.C_CHAR:
2564: case Signature.C_DOUBLE:
2565: case Signature.C_FLOAT:
2566: case Signature.C_INT:
2567: case Signature.C_LONG:
2568: case Signature.C_SHORT:
2569: case Signature.C_VOID:
2570: return scanBaseTypeSignature(string, start);
2571: case Signature.C_CAPTURE:
2572: return scanCaptureTypeSignature(string, start);
2573: case Signature.C_EXTENDS:
2574: case Signature.C_SUPER:
2575: case Signature.C_STAR:
2576: return scanTypeBoundSignature(string, start);
2577: default:
2578: throw new IllegalArgumentException();
2579: }
2580: }
2581:
2582: /**
2583: * Scans the given string for a base type signature starting at the given index
2584: * and returns the index of the last character.
2585: * <pre>
2586: * BaseTypeSignature:
2587: * <b>B</b> | <b>C</b> | <b>D</b> | <b>F</b> | <b>I</b>
2588: * | <b>J</b> | <b>S</b> | <b>V</b> | <b>Z</b>
2589: * </pre>
2590: * Note that although the base type "V" is only allowed in method return types,
2591: * there is no syntactic ambiguity. This method will accept them anywhere
2592: * without complaint.
2593: *
2594: * @param string the signature string
2595: * @param start the 0-based character index of the first character
2596: * @return the 0-based character index of the last character
2597: * @exception IllegalArgumentException if this is not a base type signature
2598: */
2599: public static int scanBaseTypeSignature(char[] string, int start) {
2600: // need a minimum 1 char
2601: if (start >= string.length) {
2602: throw new IllegalArgumentException();
2603: }
2604: char c = string[start];
2605: if ("BCDFIJSVZ".indexOf(c) >= 0) { //$NON-NLS-1$
2606: return start;
2607: } else {
2608: throw new IllegalArgumentException();
2609: }
2610: }
2611:
2612: /**
2613: * Scans the given string for an array type signature starting at the given
2614: * index and returns the index of the last character.
2615: * <pre>
2616: * ArrayTypeSignature:
2617: * <b>[</b> TypeSignature
2618: * </pre>
2619: *
2620: * @param string the signature string
2621: * @param start the 0-based character index of the first character
2622: * @return the 0-based character index of the last character
2623: * @exception IllegalArgumentException if this is not an array type signature
2624: */
2625: public static int scanArrayTypeSignature(char[] string, int start) {
2626: int length = string.length;
2627: // need a minimum 2 char
2628: if (start >= length - 1) {
2629: throw new IllegalArgumentException();
2630: }
2631: char c = string[start];
2632: if (c != Signature.C_ARRAY) {
2633: throw new IllegalArgumentException();
2634: }
2635:
2636: c = string[++start];
2637: while (c == Signature.C_ARRAY) {
2638: // need a minimum 2 char
2639: if (start >= length - 1) {
2640: throw new IllegalArgumentException();
2641: }
2642: c = string[++start];
2643: }
2644: return scanTypeSignature(string, start);
2645: }
2646:
2647: /**
2648: * Scans the given string for a capture of a wildcard type signature starting at the given
2649: * index and returns the index of the last character.
2650: * <pre>
2651: * CaptureTypeSignature:
2652: * <b>!</b> TypeBoundSignature
2653: * </pre>
2654: *
2655: * @param string the signature string
2656: * @param start the 0-based character index of the first character
2657: * @return the 0-based character index of the last character
2658: * @exception IllegalArgumentException if this is not a capture type signature
2659: */
2660: public static int scanCaptureTypeSignature(char[] string, int start) {
2661: // need a minimum 2 char
2662: if (start >= string.length - 1) {
2663: throw new IllegalArgumentException();
2664: }
2665: char c = string[start];
2666: if (c != Signature.C_CAPTURE) {
2667: throw new IllegalArgumentException();
2668: }
2669: return scanTypeBoundSignature(string, start + 1);
2670: }
2671:
2672: /**
2673: * Scans the given string for a type variable signature starting at the given
2674: * index and returns the index of the last character.
2675: * <pre>
2676: * TypeVariableSignature:
2677: * <b>T</b> Identifier <b>;</b>
2678: * </pre>
2679: *
2680: * @param string the signature string
2681: * @param start the 0-based character index of the first character
2682: * @return the 0-based character index of the last character
2683: * @exception IllegalArgumentException if this is not a type variable signature
2684: */
2685: public static int scanTypeVariableSignature(char[] string, int start) {
2686: // need a minimum 3 chars "Tx;"
2687: if (start >= string.length - 2) {
2688: throw new IllegalArgumentException();
2689: }
2690: // must start in "T"
2691: char c = string[start];
2692: if (c != Signature.C_TYPE_VARIABLE) {
2693: throw new IllegalArgumentException();
2694: }
2695: int id = scanIdentifier(string, start + 1);
2696: c = string[id + 1];
2697: if (c == Signature.C_SEMICOLON) {
2698: return id + 1;
2699: } else {
2700: throw new IllegalArgumentException();
2701: }
2702: }
2703:
2704: /**
2705: * Scans the given string for an identifier starting at the given
2706: * index and returns the index of the last character.
2707: * Stop characters are: ";", ":", "<", ">", "/", ".".
2708: *
2709: * @param string the signature string
2710: * @param start the 0-based character index of the first character
2711: * @return the 0-based character index of the last character
2712: * @exception IllegalArgumentException if this is not an identifier
2713: */
2714: public static int scanIdentifier(char[] string, int start) {
2715: // need a minimum 1 char
2716: if (start >= string.length) {
2717: throw new IllegalArgumentException();
2718: }
2719: int p = start;
2720: while (true) {
2721: char c = string[p];
2722: if (c == '<' || c == '>' || c == ':' || c == ';'
2723: || c == '.' || c == '/') {
2724: return p - 1;
2725: }
2726: p++;
2727: if (p == string.length) {
2728: return p - 1;
2729: }
2730: }
2731: }
2732:
2733: /**
2734: * Scans the given string for a class type signature starting at the given
2735: * index and returns the index of the last character.
2736: * <pre>
2737: * ClassTypeSignature:
2738: * { <b>L</b> | <b>Q</b> } Identifier
2739: * { { <b>/</b> | <b>.</b> Identifier [ <b><</b> TypeArgumentSignature* <b>></b> ] }
2740: * <b>;</b>
2741: * </pre>
2742: * Note that although all "/"-identifiers most come before "."-identifiers,
2743: * there is no syntactic ambiguity. This method will accept them without
2744: * complaint.
2745: *
2746: * @param string the signature string
2747: * @param start the 0-based character index of the first character
2748: * @return the 0-based character index of the last character
2749: * @exception IllegalArgumentException if this is not a class type signature
2750: */
2751: public static int scanClassTypeSignature(char[] string, int start) {
2752: // need a minimum 3 chars "Lx;"
2753: if (start >= string.length - 2) {
2754: throw new IllegalArgumentException();
2755: }
2756: // must start in "L" or "Q"
2757: char c = string[start];
2758: if (c != Signature.C_RESOLVED && c != Signature.C_UNRESOLVED) {
2759: return -1;
2760: }
2761: int p = start + 1;
2762: while (true) {
2763: if (p >= string.length) {
2764: throw new IllegalArgumentException();
2765: }
2766: c = string[p];
2767: if (c == Signature.C_SEMICOLON) {
2768: // all done
2769: return p;
2770: } else if (c == Signature.C_GENERIC_START) {
2771: int e = scanTypeArgumentSignatures(string, p);
2772: p = e;
2773: } else if (c == Signature.C_DOT || c == '/') {
2774: int id = scanIdentifier(string, p + 1);
2775: p = id;
2776: }
2777: p++;
2778: }
2779: }
2780:
2781: /**
2782: * Scans the given string for a type bound signature starting at the given
2783: * index and returns the index of the last character.
2784: * <pre>
2785: * TypeBoundSignature:
2786: * <b>[-+]</b> TypeSignature <b>;</b>
2787: * <b>*</b></b>
2788: * </pre>
2789: *
2790: * @param string the signature string
2791: * @param start the 0-based character index of the first character
2792: * @return the 0-based character index of the last character
2793: * @exception IllegalArgumentException if this is not a type variable signature
2794: */
2795: public static int scanTypeBoundSignature(char[] string, int start) {
2796: // need a minimum 1 char for wildcard
2797: if (start >= string.length) {
2798: throw new IllegalArgumentException();
2799: }
2800: char c = string[start];
2801: switch (c) {
2802: case Signature.C_STAR:
2803: return start;
2804: case Signature.C_SUPER:
2805: case Signature.C_EXTENDS:
2806: // need a minimum 3 chars "+[I"
2807: if (start >= string.length - 2) {
2808: throw new IllegalArgumentException();
2809: }
2810: break;
2811: default:
2812: // must start in "+/-"
2813: throw new IllegalArgumentException();
2814:
2815: }
2816: c = string[++start];
2817: switch (c) {
2818: case Signature.C_CAPTURE:
2819: return scanCaptureTypeSignature(string, start);
2820: case Signature.C_SUPER:
2821: case Signature.C_EXTENDS:
2822: return scanTypeBoundSignature(string, start);
2823: case Signature.C_RESOLVED:
2824: case Signature.C_UNRESOLVED:
2825: return scanClassTypeSignature(string, start);
2826: case Signature.C_TYPE_VARIABLE:
2827: return scanTypeVariableSignature(string, start);
2828: case Signature.C_ARRAY:
2829: return scanArrayTypeSignature(string, start);
2830: case Signature.C_STAR:
2831: return start;
2832: default:
2833: throw new IllegalArgumentException();
2834: }
2835: }
2836:
2837: /**
2838: * Scans the given string for a list of type argument signatures starting at
2839: * the given index and returns the index of the last character.
2840: * <pre>
2841: * TypeArgumentSignatures:
2842: * <b><</b> TypeArgumentSignature* <b>></b>
2843: * </pre>
2844: * Note that although there is supposed to be at least one type argument, there
2845: * is no syntactic ambiguity if there are none. This method will accept zero
2846: * type argument signatures without complaint.
2847: *
2848: * @param string the signature string
2849: * @param start the 0-based character index of the first character
2850: * @return the 0-based character index of the last character
2851: * @exception IllegalArgumentException if this is not a list of type arguments
2852: * signatures
2853: */
2854: public static int scanTypeArgumentSignatures(char[] string,
2855: int start) {
2856: // need a minimum 2 char "<>"
2857: if (start >= string.length - 1) {
2858: throw new IllegalArgumentException();
2859: }
2860: char c = string[start];
2861: if (c != Signature.C_GENERIC_START) {
2862: throw new IllegalArgumentException();
2863: }
2864: int p = start + 1;
2865: while (true) {
2866: if (p >= string.length) {
2867: throw new IllegalArgumentException();
2868: }
2869: c = string[p];
2870: if (c == Signature.C_GENERIC_END) {
2871: return p;
2872: }
2873: int e = scanTypeArgumentSignature(string, p);
2874: p = e + 1;
2875: }
2876: }
2877:
2878: /**
2879: * Scans the given string for a type argument signature starting at the given
2880: * index and returns the index of the last character.
2881: * <pre>
2882: * TypeArgumentSignature:
2883: * <b>*</b>
2884: * | <b>+</b> TypeSignature
2885: * | <b>-</b> TypeSignature
2886: * | TypeSignature
2887: * </pre>
2888: * Note that although base types are not allowed in type arguments, there is
2889: * no syntactic ambiguity. This method will accept them without complaint.
2890: *
2891: * @param string the signature string
2892: * @param start the 0-based character index of the first character
2893: * @return the 0-based character index of the last character
2894: * @exception IllegalArgumentException if this is not a type argument signature
2895: */
2896: public static int scanTypeArgumentSignature(char[] string, int start) {
2897: // need a minimum 1 char
2898: if (start >= string.length) {
2899: throw new IllegalArgumentException();
2900: }
2901: char c = string[start];
2902: switch (c) {
2903: case Signature.C_STAR:
2904: return start;
2905: case Signature.C_EXTENDS:
2906: case Signature.C_SUPER:
2907: return scanTypeBoundSignature(string, start);
2908: default:
2909: return scanTypeSignature(string, start);
2910: }
2911: }
2912:
2913: /**
2914: * Get all type arguments from an array of signatures.
2915: *
2916: * Example:
2917: * For following type X<Y<Z>,V<W>,U>.A<B> signatures is:
2918: * [
2919: * ['L','X','<','L','Y','<','L','Z',';'>',';','L','V','<','L','W',';'>',';','L','U',';',>',';'],
2920: * ['L','A','<','L','B',';','>',';']
2921: * ]
2922: * @see #splitTypeLevelsSignature(String)
2923: * Then, this method returns:
2924: * [
2925: * [
2926: * ['L','Y','<','L','Z',';'>',';'],
2927: * ['L','V','<','L','W',';'>',';'],
2928: * ['L','U',';']
2929: * ],
2930: * [
2931: * ['L','B',';']
2932: * ]
2933: * ]
2934: *
2935: * @param typeSignatures Array of signatures (one per each type levels)
2936: * @throws IllegalArgumentException If one of provided signature is malformed
2937: * @return char[][][] Array of type arguments for each signature
2938: */
2939: public final static char[][][] getAllTypeArguments(
2940: char[][] typeSignatures) {
2941: if (typeSignatures == null)
2942: return null;
2943: int length = typeSignatures.length;
2944: char[][][] typeArguments = new char[length][][];
2945: for (int i = 0; i < length; i++) {
2946: typeArguments[i] = Signature
2947: .getTypeArguments(typeSignatures[i]);
2948: }
2949: return typeArguments;
2950: }
2951:
2952: /**
2953: * Split signatures of all levels from a type unique key.
2954: *
2955: * Example:
2956: * For following type X<Y<Z>,V<W>,U>.A<B>, unique key is:
2957: * "LX<LY<LZ;>;LV<LW;>;LU;>.LA<LB;>;"
2958: *
2959: * The return splitted signatures array is:
2960: * [
2961: * ['L','X','<','L','Y','<','L','Z',';'>',';','L','V','<','L','W',';'>',';','L','U','>',';'],
2962: * ['L','A','<','L','B',';','>',';']
2963: *
2964: * @param typeSignature ParameterizedSourceType type signature
2965: * @return char[][] Array of signatures for each level of given unique key
2966: */
2967: public final static char[][] splitTypeLevelsSignature(
2968: String typeSignature) {
2969: // In case of IJavaElement signature, replace '$' by '.'
2970: char[] source = Signature.removeCapture(typeSignature
2971: .toCharArray());
2972: CharOperation.replace(source, '$', '.');
2973:
2974: // Init counters and arrays
2975: char[][] signatures = new char[10][];
2976: int signaturesCount = 0;
2977: // int[] lengthes = new int [10];
2978: int typeArgsCount = 0;
2979: int paramOpening = 0;
2980:
2981: // Scan each signature character
2982: for (int idx = 0, ln = source.length; idx < ln; idx++) {
2983: switch (source[idx]) {
2984: case '>':
2985: paramOpening--;
2986: if (paramOpening == 0) {
2987: if (signaturesCount == signatures.length) {
2988: System
2989: .arraycopy(
2990: signatures,
2991: 0,
2992: signatures = new char[signaturesCount + 10][],
2993: 0, signaturesCount);
2994: }
2995: typeArgsCount = 0;
2996: }
2997: break;
2998: case '<':
2999: paramOpening++;
3000: if (paramOpening == 1) {
3001: typeArgsCount = 1;
3002: }
3003: break;
3004: case '*':
3005: case ';':
3006: if (paramOpening == 1)
3007: typeArgsCount++;
3008: break;
3009: case '.':
3010: if (paramOpening == 0) {
3011: if (signaturesCount == signatures.length) {
3012: System
3013: .arraycopy(
3014: signatures,
3015: 0,
3016: signatures = new char[signaturesCount + 10][],
3017: 0, signaturesCount);
3018: }
3019: signatures[signaturesCount] = new char[idx + 1];
3020: System.arraycopy(source, 0,
3021: signatures[signaturesCount], 0, idx);
3022: signatures[signaturesCount][idx] = Signature.C_SEMICOLON;
3023: signaturesCount++;
3024: }
3025: break;
3026: case '/':
3027: source[idx] = '.';
3028: break;
3029: }
3030: }
3031:
3032: // Resize signatures array
3033: char[][] typeSignatures = new char[signaturesCount + 1][];
3034: typeSignatures[0] = source;
3035: for (int i = 1, j = signaturesCount - 1; i <= signaturesCount; i++, j--) {
3036: typeSignatures[i] = signatures[j];
3037: }
3038: return typeSignatures;
3039: }
3040:
3041: /*
3042: * Can throw IllegalArgumentException or ArrayIndexOutOfBoundsException
3043: */
3044: public static String toAnchor(char[] methodSignature,
3045: String methodName, boolean isVarArgs) {
3046: try {
3047: return new String(toAnchor(methodSignature, methodName
3048: .toCharArray(), isVarArgs));
3049: } catch (IllegalArgumentException e) {
3050: return null;
3051: }
3052: }
3053:
3054: private static char[] toAnchor(char[] methodSignature,
3055: char[] methodName, boolean isVargArgs) {
3056: int firstParen = CharOperation.indexOf(Signature.C_PARAM_START,
3057: methodSignature);
3058: if (firstParen == -1) {
3059: throw new IllegalArgumentException();
3060: }
3061:
3062: StringBuffer buffer = new StringBuffer(
3063: methodSignature.length + 10);
3064:
3065: // selector
3066: if (methodName != null) {
3067: buffer.append(methodName);
3068: }
3069:
3070: // parameters
3071: buffer.append('(');
3072: char[][] pts = Signature.getParameterTypes(methodSignature);
3073: for (int i = 0, max = pts.length; i < max; i++) {
3074: if (i == max - 1) {
3075: appendTypeSignatureForAnchor(pts[i], 0, buffer,
3076: isVargArgs);
3077: } else {
3078: appendTypeSignatureForAnchor(pts[i], 0, buffer, false);
3079: }
3080: if (i != pts.length - 1) {
3081: buffer.append(',');
3082: buffer.append(' ');
3083: }
3084: }
3085: buffer.append(')');
3086: char[] result = new char[buffer.length()];
3087: buffer.getChars(0, buffer.length(), result, 0);
3088: return result;
3089: }
3090:
3091: private static int appendTypeSignatureForAnchor(char[] string,
3092: int start, StringBuffer buffer, boolean isVarArgs) {
3093: // need a minimum 1 char
3094: if (start >= string.length) {
3095: throw new IllegalArgumentException();
3096: }
3097: char c = string[start];
3098: if (isVarArgs) {
3099: switch (c) {
3100: case Signature.C_ARRAY:
3101: return appendArrayTypeSignatureForAnchor(string, start,
3102: buffer, true);
3103: case Signature.C_RESOLVED:
3104: case Signature.C_TYPE_VARIABLE:
3105: case Signature.C_BOOLEAN:
3106: case Signature.C_BYTE:
3107: case Signature.C_CHAR:
3108: case Signature.C_DOUBLE:
3109: case Signature.C_FLOAT:
3110: case Signature.C_INT:
3111: case Signature.C_LONG:
3112: case Signature.C_SHORT:
3113: case Signature.C_VOID:
3114: case Signature.C_STAR:
3115: case Signature.C_EXTENDS:
3116: case Signature.C_SUPER:
3117: case Signature.C_CAPTURE:
3118: default:
3119: throw new IllegalArgumentException(); // a var args is an array type
3120: }
3121: } else {
3122: switch (c) {
3123: case Signature.C_ARRAY:
3124: return appendArrayTypeSignatureForAnchor(string, start,
3125: buffer, false);
3126: case Signature.C_RESOLVED:
3127: return appendClassTypeSignatureForAnchor(string, start,
3128: buffer);
3129: case Signature.C_TYPE_VARIABLE:
3130: int e = Util.scanTypeVariableSignature(string, start);
3131: buffer.append(string, start + 1, e - start - 1);
3132: return e;
3133: case Signature.C_BOOLEAN:
3134: buffer.append(BOOLEAN);
3135: return start;
3136: case Signature.C_BYTE:
3137: buffer.append(BYTE);
3138: return start;
3139: case Signature.C_CHAR:
3140: buffer.append(CHAR);
3141: return start;
3142: case Signature.C_DOUBLE:
3143: buffer.append(DOUBLE);
3144: return start;
3145: case Signature.C_FLOAT:
3146: buffer.append(FLOAT);
3147: return start;
3148: case Signature.C_INT:
3149: buffer.append(INT);
3150: return start;
3151: case Signature.C_LONG:
3152: buffer.append(LONG);
3153: return start;
3154: case Signature.C_SHORT:
3155: buffer.append(SHORT);
3156: return start;
3157: case Signature.C_VOID:
3158: buffer.append(VOID);
3159: return start;
3160: case Signature.C_CAPTURE:
3161: return appendCaptureTypeSignatureForAnchor(string,
3162: start, buffer);
3163: case Signature.C_STAR:
3164: case Signature.C_EXTENDS:
3165: case Signature.C_SUPER:
3166: return appendTypeArgumentSignatureForAnchor(string,
3167: start, buffer);
3168: default:
3169: throw new IllegalArgumentException();
3170: }
3171: }
3172: }
3173:
3174: private static int appendTypeArgumentSignatureForAnchor(
3175: char[] string, int start, StringBuffer buffer) {
3176: // need a minimum 1 char
3177: if (start >= string.length) {
3178: throw new IllegalArgumentException();
3179: }
3180: char c = string[start];
3181: switch (c) {
3182: case Signature.C_STAR:
3183: return start;
3184: case Signature.C_EXTENDS:
3185: return appendTypeSignatureForAnchor(string, start + 1,
3186: buffer, false);
3187: case Signature.C_SUPER:
3188: return appendTypeSignatureForAnchor(string, start + 1,
3189: buffer, false);
3190: default:
3191: return appendTypeSignatureForAnchor(string, start, buffer,
3192: false);
3193: }
3194: }
3195:
3196: private static int appendCaptureTypeSignatureForAnchor(
3197: char[] string, int start, StringBuffer buffer) {
3198: // need a minimum 2 char
3199: if (start >= string.length - 1) {
3200: throw new IllegalArgumentException();
3201: }
3202: char c = string[start];
3203: if (c != Signature.C_CAPTURE) {
3204: throw new IllegalArgumentException();
3205: }
3206: return appendTypeArgumentSignatureForAnchor(string, start + 1,
3207: buffer);
3208: }
3209:
3210: private static int appendArrayTypeSignatureForAnchor(char[] string,
3211: int start, StringBuffer buffer, boolean isVarArgs) {
3212: int length = string.length;
3213: // need a minimum 2 char
3214: if (start >= length - 1) {
3215: throw new IllegalArgumentException();
3216: }
3217: char c = string[start];
3218: if (c != Signature.C_ARRAY) {
3219: throw new IllegalArgumentException();
3220: }
3221:
3222: int index = start;
3223: c = string[++index];
3224: while (c == Signature.C_ARRAY) {
3225: // need a minimum 2 char
3226: if (index >= length - 1) {
3227: throw new IllegalArgumentException();
3228: }
3229: c = string[++index];
3230: }
3231:
3232: int e = appendTypeSignatureForAnchor(string, index, buffer,
3233: false);
3234:
3235: for (int i = 1, dims = index - start; i < dims; i++) {
3236: buffer.append('[').append(']');
3237: }
3238:
3239: if (isVarArgs) {
3240: buffer.append('.').append('.').append('.');
3241: } else {
3242: buffer.append('[').append(']');
3243: }
3244: return e;
3245: }
3246:
3247: private static int appendClassTypeSignatureForAnchor(char[] string,
3248: int start, StringBuffer buffer) {
3249: // need a minimum 3 chars "Lx;"
3250: if (start >= string.length - 2) {
3251: throw new IllegalArgumentException();
3252: }
3253: // must start in "L" or "Q"
3254: char c = string[start];
3255: if (c != Signature.C_RESOLVED && c != Signature.C_UNRESOLVED) {
3256: throw new IllegalArgumentException();
3257: }
3258: int p = start + 1;
3259: while (true) {
3260: if (p >= string.length) {
3261: throw new IllegalArgumentException();
3262: }
3263: c = string[p];
3264: switch (c) {
3265: case Signature.C_SEMICOLON:
3266: // all done
3267: return p;
3268: case Signature.C_GENERIC_START:
3269: int e = scanGenericEnd(string, p + 1);
3270: // once we hit type arguments there are no more package prefixes
3271: p = e;
3272: break;
3273: case Signature.C_DOT:
3274: buffer.append('.');
3275: break;
3276: case '/':
3277: buffer.append('/');
3278: break;
3279: case Signature.C_DOLLAR:
3280: // once we hit "$" there are no more package prefixes
3281: /**
3282: * Convert '$' in resolved type signatures into '.'.
3283: * NOTE: This assumes that the type signature is an inner type
3284: * signature. This is true in most cases, but someone can define a
3285: * non-inner type name containing a '$'.
3286: */
3287: buffer.append('.');
3288: break;
3289: default:
3290: buffer.append(c);
3291: }
3292: p++;
3293: }
3294: }
3295:
3296: private static int scanGenericEnd(char[] string, int start) {
3297: if (string[start] == Signature.C_GENERIC_END) {
3298: return start;
3299: }
3300: int length = string.length;
3301: int balance = 1;
3302: start++;
3303: while (start <= length) {
3304: switch (string[start]) {
3305: case Signature.C_GENERIC_END:
3306: balance--;
3307: if (balance == 0) {
3308: return start;
3309: }
3310: break;
3311: case Signature.C_GENERIC_START:
3312: balance++;
3313: break;
3314: }
3315: start++;
3316: }
3317: return start;
3318: }
3319: }
|