diff options
Diffstat (limited to 'bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/core/search/SearchPattern.java')
-rw-r--r-- | bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/core/search/SearchPattern.java | 3839 |
1 files changed, 1918 insertions, 1921 deletions
diff --git a/bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/core/search/SearchPattern.java b/bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/core/search/SearchPattern.java index 1b21032c..0512a378 100644 --- a/bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/core/search/SearchPattern.java +++ b/bundles/org.eclipse.wst.jsdt.core/src/org/eclipse/wst/jsdt/core/search/SearchPattern.java @@ -1,1921 +1,1918 @@ -/******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.wst.jsdt.core.search; - -import org.eclipse.wst.jsdt.core.IField; -import org.eclipse.wst.jsdt.core.IFunction; -import org.eclipse.wst.jsdt.core.IImportDeclaration; -import org.eclipse.wst.jsdt.core.IJavaScriptElement; -import org.eclipse.wst.jsdt.core.IMember; -import org.eclipse.wst.jsdt.core.IType; -import org.eclipse.wst.jsdt.core.JavaScriptModelException; -import org.eclipse.wst.jsdt.core.Signature; -import org.eclipse.wst.jsdt.core.compiler.CharOperation; -import org.eclipse.wst.jsdt.core.compiler.InvalidInputException; -import org.eclipse.wst.jsdt.internal.compiler.classfmt.ClassFileConstants; -import org.eclipse.wst.jsdt.internal.compiler.parser.Scanner; -import org.eclipse.wst.jsdt.internal.compiler.parser.ScannerHelper; -import org.eclipse.wst.jsdt.internal.compiler.parser.TerminalTokens; -import org.eclipse.wst.jsdt.internal.core.LocalVariable; -import org.eclipse.wst.jsdt.internal.core.search.indexing.IIndexConstants; -import org.eclipse.wst.jsdt.internal.core.search.matching.ConstructorPattern; -import org.eclipse.wst.jsdt.internal.core.search.matching.FieldPattern; -import org.eclipse.wst.jsdt.internal.core.search.matching.InternalSearchPattern; -import org.eclipse.wst.jsdt.internal.core.search.matching.LocalVariablePattern; -import org.eclipse.wst.jsdt.internal.core.search.matching.MatchLocator; -import org.eclipse.wst.jsdt.internal.core.search.matching.MethodPattern; -import org.eclipse.wst.jsdt.internal.core.search.matching.OrPattern; -import org.eclipse.wst.jsdt.internal.core.search.matching.PackageDeclarationPattern; -import org.eclipse.wst.jsdt.internal.core.search.matching.PackageReferencePattern; -import org.eclipse.wst.jsdt.internal.core.search.matching.QualifiedTypeDeclarationPattern; -import org.eclipse.wst.jsdt.internal.core.search.matching.SuperTypeReferencePattern; -import org.eclipse.wst.jsdt.internal.core.search.matching.TypeDeclarationPattern; -import org.eclipse.wst.jsdt.internal.core.search.matching.TypeReferencePattern; - - -/** - * A search pattern defines how search results are found. Use <code>SearchPattern.createPattern</code> - * to create a search pattern. - * <p> - * Search patterns are used during the search phase to decode index entries that were added during the indexing phase - * (see {@link SearchDocument#addIndexEntry(char[], char[])}). When an index is queried, the - * index categories and keys to consider are retrieved from the search pattern using {@link #getIndexCategories()} and - * {@link #getIndexKey()}, as well as the match rule (see {@link #getMatchRule()}). A blank pattern is - * then created (see {@link #getBlankPattern()}). This blank pattern is used as a record as follows. - * For each index entry in the given index categories and that starts with the given key, the blank pattern is fed using - * {@link #decodeIndexKey(char[])}. The original pattern is then asked if it matches the decoded key using - * {@link #matchesDecodedKey(SearchPattern)}. If it matches, a search doument is created for this index entry - * using {@link SearchParticipant#getDocument(String)}. - * - * </p><p> - * This class is intended to be subclassed by clients. A default behavior is provided for each of the methods above, that - * clients can ovveride if they wish. - * </p> - * @see #createPattern(org.eclipse.wst.jsdt.core.IJavaScriptElement, int) - * @see #createPattern(String, int, int, int) - * - * Provisional API: This class/interface is part of an interim API that is still under development and expected to - * change significantly before reaching stability. It is being made available at this early stage to solicit feedback - * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken - * (repeatedly) as the API evolves. - */ -public abstract class SearchPattern extends InternalSearchPattern { - - // Rules for pattern matching: (exact, prefix, pattern) [ | case sensitive] - /** - * Match rule: The search pattern matches exactly the search result, - * that is, the source of the search result equals the search pattern. - */ - public static final int R_EXACT_MATCH = 0; - - /** - * Match rule: The search pattern is a prefix of the search result. - */ - public static final int R_PREFIX_MATCH = 0x0001; - - /** - * Match rule: The search pattern contains one or more wild cards ('*' or '?'). - * A '*' wild-card can replace 0 or more characters in the search result. - * A '?' wild-card replaces exactly 1 character in the search result. - */ - public static final int R_PATTERN_MATCH = 0x0002; - - /** - * Match rule: The search pattern contains a regular expression. - */ - public static final int R_REGEXP_MATCH = 0x0004; - - /** - * Match rule: The search pattern matches the search result only if cases are the same. - * Can be combined to previous rules, e.g. {@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE} - */ - public static final int R_CASE_SENSITIVE = 0x0008; - - /** - * Match rule: The search pattern matches search results as raw/parameterized types/methods with same erasure. - * This mode has no effect on other javascript elements search.<br> - * Type search example: - * <ul> - * <li>pattern: <code>List<Exception></code></li> - * <li>match: <code>List<Object></code></li> - * </ul> - * Method search example: - * <ul> - * <li>declaration: <code><T>foo(T t)</code></li> - * <li>pattern: <code><Exception>foo(new Exception())</code></li> - * <li>match: <code><Object>foo(new Object())</code></li> - * </ul> - * Can be combined to all other match rules, e.g. {@link #R_CASE_SENSITIVE} | {@link #R_ERASURE_MATCH} - * This rule is not activated by default, so raw types or parameterized types with same erasure will not be found - * for pattern List<String>, - * Note that with this pattern, the match selection will be only on the erasure even for parameterized types. - * - */ - public static final int R_ERASURE_MATCH = 0x0010; - - /** - * Match rule: The search pattern matches search results as raw/parameterized types/methods with equivalent type parameters. - * This mode has no effect on other javascript elements search.<br> - * Type search example: - * <ul> - * <li>pattern: <code>List<Exception></code></li> - * <li>match: - * <ul> - * <li><code>List<? extends Throwable></code></li> - * <li><code>List<? super RuntimeException></code></li> - * <li><code>List<?></code></li> - * </ul> - * </li> - * </ul> - * Method search example: - * <ul> - * <li>declaration: <code><T>foo(T t)</code></li> - * <li>pattern: <code><Exception>foo(new Exception())</code></li> - * <li>match: - * <ul> - * <li><code><? extends Throwable>foo(new Exception())</code></li> - * <li><code><? super RuntimeException>foo(new Exception())</code></li> - * <li><code>foo(new Exception())</code></li> - * </ul> - * </ul> - * Can be combined to all other match rules, e.g. {@link #R_CASE_SENSITIVE} | {@link #R_EQUIVALENT_MATCH} - * This rule is not activated by default, so raw types or equivalent parameterized types will not be found - * for pattern List<String>, - * This mode is overridden by {@link #R_ERASURE_MATCH} as erasure matches obviously include equivalent ones. - * That means that pattern with rule set to {@link #R_EQUIVALENT_MATCH} | {@link #R_ERASURE_MATCH} - * will return same results than rule only set with {@link #R_ERASURE_MATCH}. - * - */ - public static final int R_EQUIVALENT_MATCH = 0x0020; - - /** - * Match rule: The search pattern matches exactly the search result, - * that is, the source of the search result equals the search pattern. - * - */ - public static final int R_FULL_MATCH = 0x0040; - - /** - * Match rule: The search pattern contains a Camel Case expression. - * <br> - * Examples: - * <ul> - * <li><code>NPE</code> type string pattern will match - * <code>NullPointerException</code> and <code>NpPermissionException</code> types,</li> - * <li><code>NuPoEx</code> type string pattern will only match - * <code>NullPointerException</code> type.</li> - * </ul> - * @see CharOperation#camelCaseMatch(char[], char[]) for a detailed explanation - * of Camel Case matching. - *<br> - * Can be combined to {@link #R_PREFIX_MATCH} match rule. For example, - * when prefix match rule is combined with Camel Case match rule, - * <code>"nPE"</code> pattern will match <code>nPException</code>. - *<br> - * Match rule {@link #R_PATTERN_MATCH} may also be combined but both rules - * will not be used simultaneously as they are mutually exclusive. - * Used match rule depends on whether string pattern contains specific pattern - * characters (e.g. '*' or '?') or not. If it does, then only Pattern match rule - * will be used, otherwise only Camel Case match will be used. - * For example, with <code>"NPE"</code> string pattern, search will only use - * Camel Case match rule, but with <code>N*P*E*</code> string pattern, it will - * use only Pattern match rule. - * - * - */ - public static final int R_CAMELCASE_MATCH = 0x0080; - - private static final int MODE_MASK = R_EXACT_MATCH | R_PREFIX_MATCH | R_PATTERN_MATCH | R_REGEXP_MATCH; - - private int matchRule; - -/** - * Creates a search pattern with the rule to apply for matching index keys. - * It can be exact match, prefix match, pattern match or regexp match. - * Rule can also be combined with a case sensitivity flag. - * - * @param matchRule one of {@link #R_EXACT_MATCH}, {@link #R_PREFIX_MATCH}, {@link #R_PATTERN_MATCH}, - * {@link #R_REGEXP_MATCH}, {@link #R_CAMELCASE_MATCH} combined with one of following values: - * {@link #R_CASE_SENSITIVE}, {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH}. - * e.g. {@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE} if an exact and case sensitive match is requested, - * {@link #R_PREFIX_MATCH} if a prefix non case sensitive match is requested or {@link #R_EXACT_MATCH} | {@link #R_ERASURE_MATCH} - * if a non case sensitive and erasure match is requested.<br> - * Note that {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH} have no effect - * on non-generic types/methods search.<br> - * Note also that default behavior for generic types/methods search is to find exact matches. - */ -public SearchPattern(int matchRule) { - this.matchRule = matchRule; - // Set full match implicit mode - if ((matchRule & (R_EQUIVALENT_MATCH | R_ERASURE_MATCH )) == 0) { - this.matchRule |= R_FULL_MATCH; - } -} - -/** - * Answers true if the pattern matches the given name using CamelCase rules, or false otherwise. - * CamelCase matching does NOT accept explicit wild-cards '*' and '?' and is inherently case sensitive. - * <br> - * CamelCase denotes the convention of writing compound names without spaces, and capitalizing every term. - * This function recognizes both upper and lower CamelCase, depending whether the leading character is capitalized - * or not. The leading part of an upper CamelCase pattern is assumed to contain a sequence of capitals which are appearing - * in the matching name; e.g. 'NPE' will match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern - * uses a lowercase first character. In Java, type names follow the upper CamelCase convention, whereas method or field - * names follow the lower CamelCase convention. - * <br> - * The pattern may contain lowercase characters, which will be match in a case sensitive way. These characters must - * appear in sequence in the name. For instance, 'NPExcep' will match 'NullPointerException', but not 'NullPointerExCEPTION' - * or 'NuPoEx' will match 'NullPointerException', but not 'NoPointerException'. - * <br><br> - * Examples: - * <ol> - * <li><pre> - * pattern = "NPE" - * name = NullPointerException / NoPermissionException - * result => true - * </pre> - * </li> - * <li><pre> - * pattern = "NuPoEx" - * name = NullPointerException - * result => true - * </pre> - * </li> - * <li><pre> - * pattern = "npe" - * name = NullPointerException - * result => false - * </pre> - * </li> - * </ol> - * @see CharOperation#camelCaseMatch(char[], char[]) - * Implementation has been entirely copied from this method except for array lengthes - * which were obviously replaced with calls to {@link String#length()}. - * - * @param pattern the given pattern - * @param name the given name - * @return true if the pattern matches the given name, false otherwise - * - */ -public static final boolean camelCaseMatch(String pattern, String name) { - if (pattern == null) - return true; // null pattern is equivalent to '*' - if (name == null) - return false; // null name cannot match - - return camelCaseMatch(pattern, 0, pattern.length(), name, 0, name.length()); -} - -/** - * Answers true if a sub-pattern matches the subpart of the given name using CamelCase rules, or false otherwise. - * CamelCase matching does NOT accept explicit wild-cards '*' and '?' and is inherently case sensitive. - * Can match only subset of name/pattern, considering end positions as non-inclusive. - * The subpattern is defined by the patternStart and patternEnd positions. - * <br> - * CamelCase denotes the convention of writing compound names without spaces, and capitalizing every term. - * This function recognizes both upper and lower CamelCase, depending whether the leading character is capitalized - * or not. The leading part of an upper CamelCase pattern is assumed to contain a sequence of capitals which are appearing - * in the matching name; e.g. 'NPE' will match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern - * uses a lowercase first character. In Java, type names follow the upper CamelCase convention, whereas method or field - * names follow the lower CamelCase convention. - * <br> - * The pattern may contain lowercase characters, which will be match in a case sensitive way. These characters must - * appear in sequence in the name. For instance, 'NPExcep' will match 'NullPointerException', but not 'NullPointerExCEPTION' - * or 'NuPoEx' will match 'NullPointerException', but not 'NoPointerException'. - * <br><br> - * Examples: - * <ol> - * <li><pre> - * pattern = "NPE" - * patternStart = 0 - * patternEnd = 3 - * name = NullPointerException - * nameStart = 0 - * nameEnd = 20 - * result => true - * </pre> - * </li> - * <li><pre> - * pattern = "NPE" - * patternStart = 0 - * patternEnd = 3 - * name = NoPermissionException - * nameStart = 0 - * nameEnd = 21 - * result => true - * </pre> - * </li> - * <li><pre> - * pattern = "NuPoEx" - * patternStart = 0 - * patternEnd = 6 - * name = NullPointerException - * nameStart = 0 - * nameEnd = 20 - * result => true - * </pre> - * </li> - * <li><pre> - * pattern = "NuPoEx" - * patternStart = 0 - * patternEnd = 6 - * name = NoPermissionException - * nameStart = 0 - * nameEnd = 21 - * result => false - * </pre> - * </li> - * <li><pre> - * pattern = "npe" - * patternStart = 0 - * patternEnd = 3 - * name = NullPointerException - * nameStart = 0 - * nameEnd = 20 - * result => false - * </pre> - * </li> - * </ol> - * @see CharOperation#camelCaseMatch(char[], int, int, char[], int, int) - * Implementation has been entirely copied from this method except for array lengthes - * which were obviously replaced with calls to {@link String#length()} and - * for array direct access which were replaced with calls to {@link String#charAt(int)}. - * - * @param pattern the given pattern - * @param patternStart the start index of the pattern, inclusive - * @param patternEnd the end index of the pattern, exclusive - * @param name the given name - * @param nameStart the start index of the name, inclusive - * @param nameEnd the end index of the name, exclusive - * @return true if a sub-pattern matches the subpart of the given name, false otherwise - * - */ -public static final boolean camelCaseMatch(String pattern, int patternStart, int patternEnd, String name, int nameStart, int nameEnd) { - if (name == null) - return false; // null name cannot match - if (pattern == null) - return true; // null pattern is equivalent to '*' - if (patternEnd < 0) patternEnd = pattern.length(); - if (nameEnd < 0) nameEnd = name.length(); - - if (patternEnd <= patternStart) return nameEnd <= nameStart; - if (nameEnd <= nameStart) return false; - // check first pattern char - if (name.charAt(nameStart) != pattern.charAt(patternStart)) { - // first char must strictly match (upper/lower) - return false; - } - - char patternChar, nameChar; - int iPattern = patternStart; - int iName = nameStart; - - // Main loop is on pattern characters - while (true) { - - iPattern++; - iName++; - - if (iPattern == patternEnd) { - // We have exhausted pattern, so it's a match - return true; - } - - if (iName == nameEnd){ - // We have exhausted name (and not pattern), so it's not a match - return false; - } - - // For as long as we're exactly matching, bring it on (even if it's a lower case character) - if ((patternChar = pattern.charAt(iPattern)) == name.charAt(iName)) { - continue; - } - - // If characters are not equals, then it's not a match if patternChar is lowercase - if (patternChar < ScannerHelper.MAX_OBVIOUS) { - if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[patternChar] & ScannerHelper.C_UPPER_LETTER) == 0) { - return false; - } - } - else if (Character.isJavaIdentifierPart(patternChar) && !Character.isUpperCase(patternChar)) { - return false; - } - - // patternChar is uppercase, so let's find the next uppercase in name - while (true) { - if (iName == nameEnd){ - // We have exhausted name (and not pattern), so it's not a match - return false; - } - - nameChar = name.charAt(iName); - - if (nameChar < ScannerHelper.MAX_OBVIOUS) { - if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[nameChar] & (ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_SPECIAL | ScannerHelper.C_DIGIT)) != 0) { - // nameChar is lowercase - iName++; - // nameChar is uppercase... - } else if (patternChar != nameChar) { - //.. and it does not match patternChar, so it's not a match - return false; - } else { - //.. and it matched patternChar. Back to the big loop - break; - } - } - else if (Character.isJavaIdentifierPart(nameChar) && !Character.isUpperCase(nameChar)) { - // nameChar is lowercase - iName++; - // nameChar is uppercase... - } else if (patternChar != nameChar) { - //.. and it does not match patternChar, so it's not a match - return false; - } else { - //.. and it matched patternChar. Back to the big loop - break; - } - } - // At this point, either name has been exhausted, or it is at an uppercase letter. - // Since pattern is also at an uppercase letter - } -} - -/** - * Returns a search pattern that combines the given two patterns into an - * "and" pattern. The search result will match both the left pattern and - * the right pattern. - * - * @param leftPattern the left pattern - * @param rightPattern the right pattern - * @return an "and" pattern - */ -public static SearchPattern createAndPattern(SearchPattern leftPattern, SearchPattern rightPattern) { - return MatchLocator.createAndPattern(leftPattern, rightPattern); -} - -/** - * Field pattern are formed by [declaringType.]name[ type] - * e.g. java.lang.String.serialVersionUID long - * field* - */ -private static SearchPattern createFieldPattern(String patternString, int limitTo, int matchRule,boolean isVar) { - - Scanner scanner = new Scanner(false /*comment*/, true /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3/*sourceLevel*/, null /*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/); - scanner.setSource(patternString.toCharArray()); - final int InsideDeclaringPart = 1; - final int InsideType = 2; - int lastToken = -1; - - String declaringType = null, fieldName = null; - String type = null; - int mode = InsideDeclaringPart; - int token; - try { - token = scanner.getNextToken(); - } catch (InvalidInputException e) { - return null; - } - while (token != TerminalTokens.TokenNameEOF) { - switch(mode) { - // read declaring type and fieldName - case InsideDeclaringPart : - switch (token) { - case TerminalTokens.TokenNameDOT: - if (declaringType == null) { - if (fieldName == null) return null; - declaringType = fieldName; - } else { - String tokenSource = scanner.getCurrentTokenString(); - declaringType += tokenSource + fieldName; - } - fieldName = null; - break; - case TerminalTokens.TokenNameWHITESPACE: - if (!(TerminalTokens.TokenNameWHITESPACE == lastToken || TerminalTokens.TokenNameDOT == lastToken)) - mode = InsideType; - break; - default: // all other tokens are considered identifiers (see bug 21763 Problem in JavaScript search [search]) - if (fieldName == null) - fieldName = scanner.getCurrentTokenString(); - else - fieldName += scanner.getCurrentTokenString(); - } - break; - // read type - case InsideType: - switch (token) { - case TerminalTokens.TokenNameWHITESPACE: - break; - default: // all other tokens are considered identifiers (see bug 21763 Problem in JavaScript search [search]) - if (type == null) - type = scanner.getCurrentTokenString(); - else - type += scanner.getCurrentTokenString(); - } - break; - } - lastToken = token; - try { - token = scanner.getNextToken(); - } catch (InvalidInputException e) { - return null; - } - } - if (fieldName == null) return null; - - char[] fieldNameChars = fieldName.toCharArray(); - if (fieldNameChars.length == 1 && fieldNameChars[0] == '*') fieldNameChars = null; - - char[] declaringTypeQualification = null, declaringTypeSimpleName = null; - char[] typeQualification = null, typeSimpleName = null; - - // extract declaring type infos - if (declaringType != null) { - char[] declaringTypePart = declaringType.toCharArray(); - int lastDotPosition = CharOperation.lastIndexOf('.', declaringTypePart); - if (lastDotPosition >= 0) { - declaringTypeQualification = CharOperation.subarray(declaringTypePart, 0, lastDotPosition); - if (declaringTypeQualification.length == 1 && declaringTypeQualification[0] == '*') - declaringTypeQualification = null; - declaringTypeSimpleName = CharOperation.subarray(declaringTypePart, lastDotPosition+1, declaringTypePart.length); - } else { - declaringTypeSimpleName = declaringTypePart; - } - if (declaringTypeSimpleName.length == 1 && declaringTypeSimpleName[0] == '*') - declaringTypeSimpleName = null; - } - // extract type infos - if (type != null) { - char[] typePart = type.toCharArray(); - int lastDotPosition = CharOperation.lastIndexOf('.', typePart); - if (lastDotPosition >= 0) { - typeQualification = CharOperation.subarray(typePart, 0, lastDotPosition); - if (typeQualification.length == 1 && typeQualification[0] == '*') { - typeQualification = null; - } else { - // prefix with a '*' as the full qualification could be bigger (because of an import) - typeQualification = CharOperation.concat(IIndexConstants.ONE_STAR, typeQualification); - } - typeSimpleName = CharOperation.subarray(typePart, lastDotPosition+1, typePart.length); - } else { - typeSimpleName = typePart; - } - if (typeSimpleName.length == 1 && typeSimpleName[0] == '*') - typeSimpleName = null; - } - // Create field pattern - boolean findDeclarations = false; - boolean readAccess = false; - boolean writeAccess = false; - switch (limitTo) { - case IJavaScriptSearchConstants.DECLARATIONS : - findDeclarations = true; - break; - case IJavaScriptSearchConstants.REFERENCES : - readAccess = true; - writeAccess = true; - break; - case IJavaScriptSearchConstants.READ_ACCESSES : - readAccess = true; - break; - case IJavaScriptSearchConstants.WRITE_ACCESSES : - writeAccess = true; - break; - case IJavaScriptSearchConstants.ALL_OCCURRENCES : - findDeclarations = true; - readAccess = true; - writeAccess = true; - break; - } - return new FieldPattern( - findDeclarations, - readAccess, - writeAccess, - isVar, - fieldNameChars, - declaringTypeQualification, - declaringTypeSimpleName, - typeQualification, - typeSimpleName, - matchRule,null); -} - -/** - * Method pattern are formed by:<br> - * [declaringType '.'] ['<' typeArguments '>'] selector ['(' parameterTypes ')'] [returnType] - * <br>e.g.<ul> - * <li>java.lang.Runnable.run() void</li> - * <li>main(*)</li> - * <li><String>toArray(String[])</li> - * </ul> - * Constructor pattern are formed by:<br> - * [declaringQualification '.'] ['<' typeArguments '>'] type ['(' parameterTypes ')'] - * <br>e.g.<ul> - * <li>java.lang.Object()</li> - * <li>Main(*)</li> - * <li><Exception>Sample(Exception)</li> - * </ul> - * Type arguments have the same pattern that for type patterns - * @see #createTypePattern(String,int,int,char) - */ -private static SearchPattern createMethodOrConstructorPattern(String patternString, int limitTo, int matchRule, boolean isConstructor, boolean isFunction) { - - Scanner scanner = new Scanner(false /*comment*/, true /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3/*sourceLevel*/, null /*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/); - scanner.setSource(patternString.toCharArray()); - final int InsideSelector = 1; - final int InsideTypeArguments = 2; - final int InsideParameter = 3; - final int InsideReturnType = 4; - int lastToken = -1; - - String declaringType = null, selector = null, parameterType = null; - String[] parameterTypes = null; - char[][] typeArguments = null; - String typeArgumentsString = null; - int parameterCount = -1; - String returnType = null; - boolean foundClosingParenthesis = false; - int mode = InsideSelector; - int token, argCount = 0; - try { - token = scanner.getNextToken(); - } catch (InvalidInputException e) { - return null; - } - while (token != TerminalTokens.TokenNameEOF) { - switch(mode) { - // read declaring type and selector - case InsideSelector : - if (argCount == 0) { - switch (token) { - case TerminalTokens.TokenNameLESS: - argCount++; - if (selector == null || lastToken == TerminalTokens.TokenNameDOT) { - if (typeArgumentsString != null) return null; // invalid syntax - typeArgumentsString = scanner.getCurrentTokenString(); - mode = InsideTypeArguments; - break; - } - if (declaringType == null) { - declaringType = selector; - } else { - declaringType += '.' + selector; - } - declaringType += scanner.getCurrentTokenString(); - selector = null; - break; - case TerminalTokens.TokenNameDOT: - if (typeArgumentsString != null) return null; // invalid syntax - if (declaringType == null) { - if (selector == null) return null; // invalid syntax - declaringType = selector; - } else if (selector != null) { - declaringType += scanner.getCurrentTokenString() + selector; - } - selector = null; - break; - case TerminalTokens.TokenNameLPAREN: - parameterTypes = new String[5]; - parameterCount = 0; - mode = InsideParameter; - break; - case TerminalTokens.TokenNameWHITESPACE: - switch (lastToken) { - case TerminalTokens.TokenNameWHITESPACE: - case TerminalTokens.TokenNameDOT: - case TerminalTokens.TokenNameGREATER: - case TerminalTokens.TokenNameRIGHT_SHIFT: - case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT: - break; - default: - mode = InsideReturnType; - break; - } - break; - default: // all other tokens are considered identifiers (see bug 21763 Problem in JavaScript search [search]) - if (selector == null) - selector = scanner.getCurrentTokenString(); - else - selector += scanner.getCurrentTokenString(); - break; - } - } else { - if (declaringType == null) return null; // invalid syntax - switch (token) { - case TerminalTokens.TokenNameGREATER: - case TerminalTokens.TokenNameRIGHT_SHIFT: - case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT: - argCount--; - break; - case TerminalTokens.TokenNameLESS: - argCount++; - break; - } - declaringType += scanner.getCurrentTokenString(); - } - break; - // read type arguments - case InsideTypeArguments: - if (typeArgumentsString == null) return null; // invalid syntax - typeArgumentsString += scanner.getCurrentTokenString(); - switch (token) { - case TerminalTokens.TokenNameGREATER: - case TerminalTokens.TokenNameRIGHT_SHIFT: - case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT: - argCount--; - if (argCount == 0) { - typeArguments = new char[0][0]; - mode = InsideSelector; - } - break; - case TerminalTokens.TokenNameLESS: - argCount++; - break; - } - break; - // read parameter types - case InsideParameter : - if (argCount == 0) { - switch (token) { - case TerminalTokens.TokenNameWHITESPACE: - break; - case TerminalTokens.TokenNameCOMMA: - if (parameterType == null) return null; - if (parameterTypes != null) { - if (parameterTypes.length == parameterCount) - System.arraycopy(parameterTypes, 0, parameterTypes = new String[parameterCount*2], 0, parameterCount); - parameterTypes[parameterCount++] = parameterType; - } - parameterType = null; - break; - case TerminalTokens.TokenNameRPAREN: - foundClosingParenthesis = true; - if (parameterType != null && parameterTypes != null) { - if (parameterTypes.length == parameterCount) - System.arraycopy(parameterTypes, 0, parameterTypes = new String[parameterCount*2], 0, parameterCount); - parameterTypes[parameterCount++] = parameterType; - } - mode = isConstructor ? InsideTypeArguments : InsideReturnType; - break; - case TerminalTokens.TokenNameLESS: - argCount++; - if (parameterType == null) return null; // invalid syntax - // fall through next case to add token - default: // all other tokens are considered identifiers (see bug 21763 Problem in JavaScript search [search]) - if (parameterType == null) - parameterType = scanner.getCurrentTokenString(); - else - parameterType += scanner.getCurrentTokenString(); - } - } else { - if (parameterType == null) return null; // invalid syntax - switch (token) { - case TerminalTokens.TokenNameGREATER: - case TerminalTokens.TokenNameRIGHT_SHIFT: - case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT: - argCount--; - break; - case TerminalTokens.TokenNameLESS: - argCount++; - break; - } - parameterType += scanner.getCurrentTokenString(); - } - break; - // read return type - case InsideReturnType: - if (argCount == 0) { - switch (token) { - case TerminalTokens.TokenNameWHITESPACE: - break; - case TerminalTokens.TokenNameLPAREN: - parameterTypes = new String[5]; - parameterCount = 0; - mode = InsideParameter; - break; - case TerminalTokens.TokenNameLESS: - argCount++; - if (returnType == null) return null; // invalid syntax - // fall through next case to add token - default: // all other tokens are considered identifiers (see bug 21763 Problem in JavaScript search [search]) - if (returnType == null) - returnType = scanner.getCurrentTokenString(); - else - returnType += scanner.getCurrentTokenString(); - } - } else { - if (returnType == null) return null; // invalid syntax - switch (token) { - case TerminalTokens.TokenNameGREATER: - case TerminalTokens.TokenNameRIGHT_SHIFT: - case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT: - argCount--; - break; - case TerminalTokens.TokenNameLESS: - argCount++; - break; - } - returnType += scanner.getCurrentTokenString(); - } - break; - } - lastToken = token; - try { - token = scanner.getNextToken(); - } catch (InvalidInputException e) { - return null; - } - } - // parenthesis mismatch - if (parameterCount>0 && !foundClosingParenthesis) return null; - // type arguments mismatch - if (argCount > 0) return null; - - char[] selectorChars = null; - if (isConstructor) { - // retrieve type for constructor patterns - if (declaringType == null) - declaringType = selector; - else if (selector != null) - declaringType += '.' + selector; - } else { - // get selector chars - if (selector == null) return null; - selectorChars = selector.toCharArray(); - if (selectorChars.length == 1 && selectorChars[0] == '*') - selectorChars = null; - } - - char[] declaringTypeQualification = null, declaringTypeSimpleName = null; - char[] returnTypeQualification = null, returnTypeSimpleName = null; - char[][] parameterTypeQualifications = null, parameterTypeSimpleNames = null; - // Signatures - String declaringTypeSignature = null; - String returnTypeSignature = null; - String[] parameterTypeSignatures = null; - - // extract declaring type infos - if (declaringType != null) { - // get declaring type part and signature - char[] declaringTypePart = null; - try { - declaringTypeSignature = Signature.createTypeSignature(declaringType, false); - declaringTypePart = declaringType.toCharArray(); - } - catch (IllegalArgumentException iae) { - // declaring type is invalid - return null; - } - int lastDotPosition = CharOperation.lastIndexOf('.', declaringTypePart); - if (lastDotPosition >= 0) { - declaringTypeQualification = CharOperation.subarray(declaringTypePart, 0, lastDotPosition); - if (declaringTypeQualification.length == 1 && declaringTypeQualification[0] == '*') - declaringTypeQualification = null; - declaringTypeSimpleName = CharOperation.subarray(declaringTypePart, lastDotPosition+1, declaringTypePart.length); - } else { - declaringTypeSimpleName = declaringTypePart; - } - if (declaringTypeSimpleName.length == 1 && declaringTypeSimpleName[0] == '*') - declaringTypeSimpleName = null; - } - // extract parameter types infos - if (parameterCount >= 0) { - parameterTypeQualifications = new char[parameterCount][]; - parameterTypeSimpleNames = new char[parameterCount][]; - parameterTypeSignatures = new String[parameterCount]; - for (int i = 0; i < parameterCount; i++) { - // get parameter type part and signature - char[] parameterTypePart = null; - try { - if (parameterTypes != null) { - parameterTypeSignatures[i] = Signature.createTypeSignature(parameterTypes[i], false); - parameterTypePart = parameterTypes[i].toCharArray(); - } - } - catch (IllegalArgumentException iae) { - // string is not a valid type syntax - return null; - } - int lastDotPosition = parameterTypePart==null ? -1 : CharOperation.lastIndexOf('.', parameterTypePart); - if (parameterTypePart != null && lastDotPosition >= 0) { - parameterTypeQualifications[i] = CharOperation.subarray(parameterTypePart, 0, lastDotPosition); - if (parameterTypeQualifications[i].length == 1 && parameterTypeQualifications[i][0] == '*') { - parameterTypeQualifications[i] = null; - } else { - // prefix with a '*' as the full qualification could be bigger (because of an import) - parameterTypeQualifications[i] = CharOperation.concat(IIndexConstants.ONE_STAR, parameterTypeQualifications[i]); - } - parameterTypeSimpleNames[i] = CharOperation.subarray(parameterTypePart, lastDotPosition+1, parameterTypePart.length); - } else { - parameterTypeQualifications[i] = null; - parameterTypeSimpleNames[i] = parameterTypePart; - } - if (parameterTypeSimpleNames[i].length == 1 && parameterTypeSimpleNames[i][0] == '*') - parameterTypeSimpleNames[i] = null; - } - } - // extract return type infos - if (returnType != null) { - // get return type part and signature - char[] returnTypePart = null; - try { - returnTypeSignature = Signature.createTypeSignature(returnType, false); - returnTypePart = returnType.toCharArray(); - } - catch (IllegalArgumentException iae) { - // declaring type is invalid - return null; - } - int lastDotPosition = CharOperation.lastIndexOf('.', returnTypePart); - if (lastDotPosition >= 0) { - returnTypeQualification = CharOperation.subarray(returnTypePart, 0, lastDotPosition); - if (returnTypeQualification.length == 1 && returnTypeQualification[0] == '*') { - returnTypeQualification = null; - } else { - // because of an import - returnTypeQualification = CharOperation.concat(IIndexConstants.ONE_STAR, returnTypeQualification); - } - returnTypeSimpleName = CharOperation.subarray(returnTypePart, lastDotPosition+1, returnTypePart.length); - } else { - returnTypeSimpleName = returnTypePart; - } - if (returnTypeSimpleName.length == 1 && returnTypeSimpleName[0] == '*') - returnTypeSimpleName = null; - } - // Create method/constructor pattern - boolean findDeclarations = true; - boolean findReferences = true; - switch (limitTo) { - case IJavaScriptSearchConstants.DECLARATIONS : - findReferences = false; - break; - case IJavaScriptSearchConstants.REFERENCES : - findDeclarations = false; - break; - case IJavaScriptSearchConstants.ALL_OCCURRENCES : - break; - } - if (isConstructor) { - return new ConstructorPattern( - findDeclarations, - findReferences, - declaringTypeSimpleName, - declaringTypeQualification, - declaringTypeSignature, - parameterTypeQualifications, - parameterTypeSimpleNames, - parameterTypeSignatures, - typeArguments, - matchRule); - } else { - return new MethodPattern( - findDeclarations, - findReferences, - isFunction, - selectorChars, - declaringTypeQualification, - declaringTypeSimpleName, - declaringTypeSignature, - returnTypeQualification, - returnTypeSimpleName, - returnTypeSignature, - parameterTypeQualifications, - parameterTypeSimpleNames, - parameterTypeSignatures, - typeArguments, - matchRule); - } -} - -/** - * Returns a search pattern that combines the given two patterns into an - * "or" pattern. The search result will match either the left pattern or the - * right pattern. - * - * @param leftPattern the left pattern - * @param rightPattern the right pattern - * @return an "or" pattern - */ -public static SearchPattern createOrPattern(SearchPattern leftPattern, SearchPattern rightPattern) { - return new OrPattern(leftPattern, rightPattern); -} - -private static SearchPattern createPackagePattern(String patternString, int limitTo, int matchRule) { - switch (limitTo) { - case IJavaScriptSearchConstants.DECLARATIONS : - return new PackageDeclarationPattern(patternString.toCharArray(), matchRule); - case IJavaScriptSearchConstants.REFERENCES : - return new PackageReferencePattern(patternString.toCharArray(), matchRule); - case IJavaScriptSearchConstants.ALL_OCCURRENCES : - return new OrPattern( - new PackageDeclarationPattern(patternString.toCharArray(), matchRule), - new PackageReferencePattern(patternString.toCharArray(), matchRule) - ); - } - return null; -} - -/** - * Returns a search pattern based on a given string pattern. The string patterns support '*' wild-cards. - * The remaining parameters are used to narrow down the type of expected results. - * - * <br> - * Examples: - * <ul> - * <li>search for case insensitive references to <code>Object</code>: - * <code>createSearchPattern("Object", TYPE, REFERENCES, false);</code></li> - * <li>search for case sensitive references to exact <code>Object()</code> constructor: - * <code>createSearchPattern("java.lang.Object()", CONSTRUCTOR, REFERENCES, true);</code></li> - * <li>search for implementers of <code>java.lang.Runnable</code>: - * <code>createSearchPattern("java.lang.Runnable", TYPE, IMPLEMENTORS, true);</code></li> - * </ul> - * @param stringPattern the given pattern - * @param searchFor determines the nature of the searched elements - * <ul> - * <li>{@link IJavaScriptSearchConstants#CLASS}: only look for classes</li> - * <li>{@link IJavaScriptSearchConstants#INTERFACE}: only look for interfaces</li> - * <li>{@link IJavaScriptSearchConstants#ENUM}: only look for enumeration</li> - * <li>{@link IJavaScriptSearchConstants#ANNOTATION_TYPE}: only look for annotation type</li> - * <li>{@link IJavaScriptSearchConstants#CLASS_AND_ENUM}: only look for classes and enumerations</li> - * <li>{@link IJavaScriptSearchConstants#CLASS_AND_INTERFACE}: only look for classes and interfaces</li> - * <li>{@link IJavaScriptSearchConstants#TYPE}: look for all types (ie. classes, interfaces, enum and annotation types)</li> - * <li>{@link IJavaScriptSearchConstants#FIELD}: look for fields</li> - * <li>{@link IJavaScriptSearchConstants#METHOD}: look for methods</li> - * <li>{@link IJavaScriptSearchConstants#CONSTRUCTOR}: look for constructors</li> - * <li>{@link IJavaScriptSearchConstants#PACKAGE}: look for packages</li> - * </ul> - * @param limitTo determines the nature of the expected matches - * <ul> - * <li>{@link IJavaScriptSearchConstants#DECLARATIONS}: will search declarations matching - * with the corresponding element. In case the element is a method, declarations of matching - * methods in subtypes will also be found, allowing to find declarations of abstract methods, etc.<br> - * Note that additional flags {@link IJavaScriptSearchConstants#IGNORE_DECLARING_TYPE} and - * {@link IJavaScriptSearchConstants#IGNORE_RETURN_TYPE} are ignored for string patterns. - * This is due to the fact that client may omit to define them in string pattern to have same behavior. - * </li> - * <li>{@link IJavaScriptSearchConstants#REFERENCES}: will search references to the given element.</li> - * <li>{@link IJavaScriptSearchConstants#ALL_OCCURRENCES}: will search for either declarations or - * references as specified above. - * </li> - * <li>{@link IJavaScriptSearchConstants#IMPLEMENTORS}: for types, will find all types - * which directly implement/extend a given interface. - * Note that types may be only classes or only interfaces if {@link IJavaScriptSearchConstants#CLASS } or - * {@link IJavaScriptSearchConstants#INTERFACE} is respectively used instead of {@link IJavaScriptSearchConstants#TYPE}. - * </li> - * </ul> - * @param matchRule one of {@link #R_EXACT_MATCH}, {@link #R_PREFIX_MATCH}, {@link #R_PATTERN_MATCH}, - * {@link #R_REGEXP_MATCH}, {@link #R_CAMELCASE_MATCH} combined with one of following values: - * {@link #R_CASE_SENSITIVE}, {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH}. - * e.g. {@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE} if an exact and case sensitive match is requested, - * {@link #R_PREFIX_MATCH} if a prefix non case sensitive match is requested or {@link #R_EXACT_MATCH} | {@link #R_ERASURE_MATCH} - * if a non case sensitive and erasure match is requested.<br> - * Note that {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH} have no effect - * on non-generic types/methods search.<br> - * Note also that default behavior for generic types/methods search is to find exact matches. - * @return a search pattern on the given string pattern, or <code>null</code> if the string pattern is ill-formed - */ -public static SearchPattern createPattern(String stringPattern, int searchFor, int limitTo, int matchRule) { - if (stringPattern == null || stringPattern.length() == 0) return null; - - if ((matchRule = validateMatchRule(stringPattern, matchRule)) == -1) { - return null; - } - - // Ignore additional nature flags - limitTo &= ~(IJavaScriptSearchConstants.IGNORE_DECLARING_TYPE+IJavaScriptSearchConstants.IGNORE_RETURN_TYPE); - - switch (searchFor) { - case IJavaScriptSearchConstants.CLASS: - return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.CLASS_SUFFIX); - case IJavaScriptSearchConstants.TYPE: - return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.TYPE_SUFFIX); - case IJavaScriptSearchConstants.FUNCTION: - return createMethodOrConstructorPattern(stringPattern, limitTo, matchRule, false/*not a constructor*/,true); - case IJavaScriptSearchConstants.METHOD: - return createMethodOrConstructorPattern(stringPattern, limitTo, matchRule, false/*not a constructor*/,false); - case IJavaScriptSearchConstants.CONSTRUCTOR: - return createMethodOrConstructorPattern(stringPattern, limitTo, matchRule, true/*constructor*/,false); - case IJavaScriptSearchConstants.FIELD: - return createFieldPattern(stringPattern, limitTo, matchRule,false); - case IJavaScriptSearchConstants.VAR: - return createFieldPattern(stringPattern, limitTo, matchRule,true); - case IJavaScriptSearchConstants.PACKAGE: - return createPackagePattern(stringPattern, limitTo, matchRule); - } - return null; -} - -/** - * Returns a search pattern based on a given JavaScript element. - * The pattern is used to trigger the appropriate search. - * <br> - * Note that for generic searches, the returned pattern consider {@link #R_ERASURE_MATCH} matches. - * If other kind of generic matches (ie. {@link #R_EXACT_MATCH} or {@link #R_EQUIVALENT_MATCH}) - * are expected, {@link #createPattern(IJavaScriptElement, int, int)} method need to be used instead with - * the explicit match rule specified. - * <br> - * The pattern can be parameterized as follows: - * - * @param element the JavaScript element the search pattern is based on - * @param limitTo determines the nature of the expected matches - * <ul> - * <li>{@link IJavaScriptSearchConstants#DECLARATIONS}: will search declarations matching - * with the corresponding element. In case the element is a method, declarations of matching - * methods in subtypes will also be found, allowing to find declarations of abstract methods, etc. - * Some additional flags may be specified while searching declaration: - * <ul> - * <li>{@link IJavaScriptSearchConstants#IGNORE_DECLARING_TYPE}: declaring type will be ignored - * during the search.<br> - * For example using following test case: - * <pre> - * class A { A method() { return null; } } - * class B extends A { B method() { return null; } } - * class C { A method() { return null; } } - * </pre> - * search for <code>method</code> declaration with this flag - * will return 2 matches: in A and in C - * </li> - * <li>{@link IJavaScriptSearchConstants#IGNORE_RETURN_TYPE}: return type will be ignored - * during the search.<br> - * Using same example, search for <code>method</code> declaration with this flag - * will return 2 matches: in A and in B. - * </li> - * </ul> - * Note that these two flags may be combined and both declaring and return types can be ignored - * during the search. Then, using same example, search for <code>method</code> declaration - * with these 2 flags will return 3 matches: in A, in B and in C - * </li> - * <li>{@link IJavaScriptSearchConstants#REFERENCES}: will search references to the given element.</li> - * <li>{@link IJavaScriptSearchConstants#ALL_OCCURRENCES}: will search for either declarations or - * references as specified above. - * </li> - * <li>{@link IJavaScriptSearchConstants#IMPLEMENTORS}: for types, will find all types - * which directly implement/extend a given interface. - * </li> - * </ul> - * @return a search pattern for a JavaScript element or <code>null</code> if the given element is ill-formed - */ -public static SearchPattern createPattern(IJavaScriptElement element, int limitTo) { - return createPattern(element, limitTo, R_EXACT_MATCH | R_CASE_SENSITIVE | R_ERASURE_MATCH); -} - -/** - * Returns a search pattern based on a given JavaScript element. - * The pattern is used to trigger the appropriate search, and can be parameterized as follows: - * - * @param element the JavaScript element the search pattern is based on - * @param limitTo determines the nature of the expected matches - * <ul> - * <li>{@link IJavaScriptSearchConstants#DECLARATIONS}: will search declarations matching - * with the corresponding element. In case the element is a method, declarations of matching - * methods in subtypes will also be found, allowing to find declarations of abstract methods, etc. - * Some additional flags may be specified while searching declaration: - * <ul> - * <li>{@link IJavaScriptSearchConstants#IGNORE_DECLARING_TYPE}: declaring type will be ignored - * during the search.<br> - * For example using following test case: - * <pre> - * class A { A method() { return null; } } - * class B extends A { B method() { return null; } } - * class C { A method() { return null; } } - * </pre> - * search for <code>method</code> declaration with this flag - * will return 2 matches: in A and in C - * </li> - * <li>{@link IJavaScriptSearchConstants#IGNORE_RETURN_TYPE}: return type will be ignored - * during the search.<br> - * Using same example, search for <code>method</code> declaration with this flag - * will return 2 matches: in A and in B. - * </li> - * </ul> - * Note that these two flags may be combined and both declaring and return types can be ignored - * during the search. Then, using same example, search for <code>method</code> declaration - * with these 2 flags will return 3 matches: in A, in B and in C - * </li> - * <li>{@link IJavaScriptSearchConstants#REFERENCES}: will search references to the given element.</li> - * <li>{@link IJavaScriptSearchConstants#ALL_OCCURRENCES}: will search for either declarations or - * references as specified above. - * </li> - * <li>{@link IJavaScriptSearchConstants#IMPLEMENTORS}: for types, will find all types - * which directly implement/extend a given interface. - * </li> - * </ul> - * @param matchRule one of {@link #R_EXACT_MATCH}, {@link #R_PREFIX_MATCH}, {@link #R_PATTERN_MATCH}, - * {@link #R_REGEXP_MATCH}, {@link #R_CAMELCASE_MATCH} combined with one of following values: - * {@link #R_CASE_SENSITIVE}, {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH}. - * e.g. {@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE} if an exact and case sensitive match is requested, - * {@link #R_PREFIX_MATCH} if a prefix non case sensitive match is requested or {@link #R_EXACT_MATCH} |{@link #R_ERASURE_MATCH} - * if a non case sensitive and erasure match is requested.<br> - * Note that {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH} have no effect on non-generic types - * or methods search.<br> - * Note also that default behavior for generic types or methods is to find exact matches. - * @return a search pattern for a JavaScript element or <code>null</code> if the given element is ill-formed - * - */ -public static SearchPattern createPattern(IJavaScriptElement element, int limitTo, int matchRule) { - SearchPattern searchPattern = null; - int lastDot; - boolean ignoreDeclaringType = false; - boolean ignoreReturnType = false; - int maskedLimitTo = limitTo & ~(IJavaScriptSearchConstants.IGNORE_DECLARING_TYPE+IJavaScriptSearchConstants.IGNORE_RETURN_TYPE); - if (maskedLimitTo == IJavaScriptSearchConstants.DECLARATIONS || maskedLimitTo == IJavaScriptSearchConstants.ALL_OCCURRENCES) { - ignoreDeclaringType = (limitTo & IJavaScriptSearchConstants.IGNORE_DECLARING_TYPE) != 0; - ignoreReturnType = (limitTo & IJavaScriptSearchConstants.IGNORE_RETURN_TYPE) != 0; - } - char[] declaringSimpleName = null; - char[] declaringQualification = null; - boolean isVar=false; - boolean isFunction=false; - switch (element.getElementType()) { - case IJavaScriptElement.FIELD : - IField field = (IField) element; - IType declaringClassForField = field.getDeclaringType(); - isVar=(declaringClassForField==null); - if (!ignoreDeclaringType) { - if (declaringClassForField!=null) - { - declaringSimpleName = declaringClassForField.getElementName().toCharArray(); - declaringQualification = declaringClassForField.getPackageFragment().getElementName().toCharArray(); - char[][] enclosingNames = enclosingTypeNames(declaringClassForField); - if (enclosingNames.length > 0) { - declaringQualification = CharOperation.concat(declaringQualification, CharOperation.concatWith(enclosingNames, '.'), '.'); - } - } - } - char[] name = field.getElementName().toCharArray(); - char[] typeSimpleName = null; - char[] typeQualification = null; - String typeSignature = null; - if (!ignoreReturnType) { - try { - typeSignature = field.getTypeSignature(); - char[] signature = typeSignature.toCharArray(); - char[] typeErasure = Signature.toCharArray(signature); - CharOperation.replace(typeErasure, '$', '.'); - if ((lastDot = CharOperation.lastIndexOf('.', typeErasure)) == -1) { - typeSimpleName = typeErasure; - } else { - typeSimpleName = CharOperation.subarray(typeErasure, lastDot + 1, typeErasure.length); - typeQualification = CharOperation.subarray(typeErasure, 0, lastDot); - if (!field.isBinary()) { - // prefix with a '*' as the full qualification could be bigger (because of an import) - typeQualification = CharOperation.concat(IIndexConstants.ONE_STAR, typeQualification); - } - } - } catch (JavaScriptModelException e) { - return null; - } - } - - // Create field pattern - boolean findDeclarations = false; - boolean readAccess = false; - boolean writeAccess = false; - switch (maskedLimitTo) { - case IJavaScriptSearchConstants.DECLARATIONS : - findDeclarations = true; - break; - case IJavaScriptSearchConstants.REFERENCES : - readAccess = true; - writeAccess = true; - break; - case IJavaScriptSearchConstants.READ_ACCESSES : - readAccess = true; - break; - case IJavaScriptSearchConstants.WRITE_ACCESSES : - writeAccess = true; - break; - case IJavaScriptSearchConstants.ALL_OCCURRENCES : - findDeclarations = true; - readAccess = true; - writeAccess = true; - break; - } - searchPattern = - new FieldPattern( - findDeclarations, - readAccess, - writeAccess, - isVar, - name, - declaringQualification, - declaringSimpleName, - typeQualification, - typeSimpleName, - typeSignature, - matchRule,field); - break; - case IJavaScriptElement.IMPORT_DECLARATION : - String elementName = element.getElementName(); - lastDot = elementName.lastIndexOf('.'); - if (lastDot == -1) return null; // invalid import declaration - IImportDeclaration importDecl = (IImportDeclaration)element; - if (importDecl.isOnDemand()) { - searchPattern = createPackagePattern(elementName.substring(0, lastDot), maskedLimitTo, matchRule); - } else { - searchPattern = - createTypePattern( - elementName.substring(lastDot+1).toCharArray(), - elementName.substring(0, lastDot).toCharArray(), - null, - null, - null, - maskedLimitTo, - matchRule); - } - break; - case IJavaScriptElement.LOCAL_VARIABLE : - LocalVariable localVar = (LocalVariable) element; - boolean findVarDeclarations = false; - boolean findVarReadAccess = false; - boolean findVarWriteAccess = false; - switch (maskedLimitTo) { - case IJavaScriptSearchConstants.DECLARATIONS : - findVarDeclarations = true; - break; - case IJavaScriptSearchConstants.REFERENCES : - findVarReadAccess = true; - findVarWriteAccess = true; - break; - case IJavaScriptSearchConstants.READ_ACCESSES : - findVarReadAccess = true; - break; - case IJavaScriptSearchConstants.WRITE_ACCESSES : - findVarWriteAccess = true; - break; - case IJavaScriptSearchConstants.ALL_OCCURRENCES : - findVarDeclarations = true; - findVarReadAccess = true; - findVarWriteAccess = true; - break; - } - searchPattern = - new LocalVariablePattern( - findVarDeclarations, - findVarReadAccess, - findVarWriteAccess, - localVar, - matchRule); - break; - case IJavaScriptElement.METHOD : - IFunction method = (IFunction) element; - boolean isConstructor; - try { - isConstructor = method.isConstructor(); - } catch (JavaScriptModelException e) { - return null; - } - IType declaringClass = method.getDeclaringType(); - - if (declaringClass!=null) { - if (ignoreDeclaringType) { - if (isConstructor) - declaringSimpleName = declaringClass.getElementName() - .toCharArray(); - } else { - declaringSimpleName = declaringClass.getElementName() - .toCharArray(); - declaringQualification = declaringClass - .getPackageFragment().getElementName() - .toCharArray(); - char[][] enclosingNames = enclosingTypeNames(declaringClass); - if (enclosingNames.length > 0) { - declaringQualification = CharOperation.concat( - declaringQualification, CharOperation - .concatWith(enclosingNames, '.'), '.'); - } - } - } - else - isFunction=true; - char[] selector = method.getElementName().toCharArray(); - char[] returnSimpleName = null; - char[] returnQualification = null; - String returnSignature = null; - if (!ignoreReturnType) { - try { - returnSignature = method.getReturnType(); - char[] signature = returnSignature.toCharArray(); - char[] returnErasure = Signature.toCharArray(signature); - CharOperation.replace(returnErasure, '$', '.'); - if ((lastDot = CharOperation.lastIndexOf('.', returnErasure)) == -1) { - returnSimpleName = returnErasure; - } else { - returnSimpleName = CharOperation.subarray(returnErasure, lastDot + 1, returnErasure.length); - returnQualification = CharOperation.subarray(returnErasure, 0, lastDot); - if (!method.isBinary()) { - // prefix with a '*' as the full qualification could be bigger (because of an import) - CharOperation.concat(IIndexConstants.ONE_STAR, returnQualification); - } - } - } catch (JavaScriptModelException e) { - return null; - } - } - String[] parameterTypes = method.getParameterTypes(); - int paramCount = parameterTypes.length; - char[][] parameterSimpleNames = new char[paramCount][]; - char[][] parameterQualifications = new char[paramCount][]; - String[] parameterSignatures = new String[paramCount]; - for (int i = 0; i < paramCount; i++) { - parameterSignatures[i] = parameterTypes[i]; - char[] signature = parameterSignatures[i].toCharArray(); - char[] paramErasure = Signature.toCharArray(signature); - CharOperation.replace(paramErasure, '$', '.'); - if ((lastDot = CharOperation.lastIndexOf('.', paramErasure)) == -1) { - parameterSimpleNames[i] = paramErasure; - parameterQualifications[i] = null; - } else { - parameterSimpleNames[i] = CharOperation.subarray(paramErasure, lastDot + 1, paramErasure.length); - parameterQualifications[i] = CharOperation.subarray(paramErasure, 0, lastDot); - if (!method.isBinary()) { - // prefix with a '*' as the full qualification could be bigger (because of an import) - CharOperation.concat(IIndexConstants.ONE_STAR, parameterQualifications[i]); - } - } - } - - // Create method/constructor pattern - boolean findMethodDeclarations = true; - boolean findMethodReferences = true; - switch (maskedLimitTo) { - case IJavaScriptSearchConstants.DECLARATIONS : - findMethodReferences = false; - break; - case IJavaScriptSearchConstants.REFERENCES : - findMethodDeclarations = false; - break; - case IJavaScriptSearchConstants.ALL_OCCURRENCES : - break; - } - if (isConstructor) { - searchPattern = - new ConstructorPattern( - findMethodDeclarations, - findMethodReferences, - declaringSimpleName, - declaringQualification, - parameterQualifications, - parameterSimpleNames, - parameterSignatures, - method, - matchRule); - } else { - searchPattern = - new MethodPattern( - findMethodDeclarations, - findMethodReferences, - isFunction, - selector, - declaringQualification, - declaringSimpleName, - returnQualification, - returnSimpleName, - returnSignature, - parameterQualifications, - parameterSimpleNames, - parameterSignatures, - method, - matchRule); - } - break; - case IJavaScriptElement.TYPE : - IType type = (IType)element; - searchPattern = createTypePattern( - type.getElementName().toCharArray(), - type.getPackageFragment().getElementName().toCharArray(), - ignoreDeclaringType ? null : enclosingTypeNames(type), - null, - type, - maskedLimitTo, - matchRule); - break; - case IJavaScriptElement.PACKAGE_FRAGMENT : - searchPattern = createPackagePattern(element.getElementName(), maskedLimitTo, matchRule); - break; - } - if (searchPattern != null) - MatchLocator.setFocus(searchPattern, element); - return searchPattern; -} - -private static SearchPattern createTypePattern(char[] simpleName, char[] packageName, char[][] enclosingTypeNames, String typeSignature, IType type, int limitTo, int matchRule) { - switch (limitTo) { - case IJavaScriptSearchConstants.DECLARATIONS : - return new TypeDeclarationPattern( - packageName, - enclosingTypeNames, - simpleName, - IIndexConstants.TYPE_SUFFIX, - matchRule); - case IJavaScriptSearchConstants.REFERENCES : - if (type != null) { - return new TypeReferencePattern( - CharOperation.concatWith(packageName, enclosingTypeNames, '.'), - simpleName, - type, - matchRule); - } - return new TypeReferencePattern( - CharOperation.concatWith(packageName, enclosingTypeNames, '.'), - simpleName, - typeSignature, - matchRule); - case IJavaScriptSearchConstants.IMPLEMENTORS : - return new SuperTypeReferencePattern( - CharOperation.concatWith(packageName, enclosingTypeNames, '.'), - simpleName, - matchRule); - case IJavaScriptSearchConstants.ALL_OCCURRENCES : - return new OrPattern( - new TypeDeclarationPattern( - packageName, - enclosingTypeNames, - simpleName, - IIndexConstants.TYPE_SUFFIX, - matchRule), - (type != null) - ? new TypeReferencePattern( - CharOperation.concatWith(packageName, enclosingTypeNames, '.'), - simpleName, - type, - matchRule) - : new TypeReferencePattern( - CharOperation.concatWith(packageName, enclosingTypeNames, '.'), - simpleName, - typeSignature, - matchRule) - ); - } - return null; -} -/** - * Type pattern are formed by [qualification '.']type [typeArguments]. - * e.g. java.lang.Object - * Runnable - * List<String> - */ -private static SearchPattern createTypePattern(String patternString, int limitTo, int matchRule, char indexSuffix) { - - Scanner scanner = new Scanner(false /*comment*/, true /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3/*sourceLevel*/, null /*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/); - scanner.setSource(patternString.toCharArray()); - String type = null; - int token; - try { - token = scanner.getNextToken(); - } catch (InvalidInputException e) { - return null; - } - int argCount = 0; - while (token != TerminalTokens.TokenNameEOF) { - if (argCount == 0) { - switch (token) { - case TerminalTokens.TokenNameWHITESPACE: - break; - case TerminalTokens.TokenNameLESS: - argCount++; - // fall through default case to add token to type - default: // all other tokens are considered identifiers (see bug 21763 Problem in JavaScript search [search]) - if (type == null) - type = scanner.getCurrentTokenString(); - else - type += scanner.getCurrentTokenString(); - } - } else { - switch (token) { - case TerminalTokens.TokenNameGREATER: - case TerminalTokens.TokenNameRIGHT_SHIFT: - case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT: - argCount--; - break; - case TerminalTokens.TokenNameLESS: - argCount++; - break; - } - if (type == null) return null; // invalid syntax - type += scanner.getCurrentTokenString(); - } - try { - token = scanner.getNextToken(); - } catch (InvalidInputException e) { - return null; - } - } - if (type == null) return null; - String typeSignature = null; - char[] qualificationChars = null, typeChars = null; - - // get type part and signature - char[] typePart = null; - try { - typeSignature = Signature.createTypeSignature(type, false); - typePart = type.toCharArray(); - } - catch (IllegalArgumentException iae) { - // string is not a valid type syntax - return null; - } - - // get qualification name - int lastDotPosition = CharOperation.lastIndexOf('.', typePart); - if (lastDotPosition >= 0) { - qualificationChars = CharOperation.subarray(typePart, 0, lastDotPosition); - if (qualificationChars.length == 1 && qualificationChars[0] == '*') - qualificationChars = null; - typeChars = CharOperation.subarray(typePart, lastDotPosition+1, typePart.length); - } else { - typeChars = typePart; - } - if (typeChars.length == 1 && typeChars[0] == '*') { - typeChars = null; - } - switch (limitTo) { - case IJavaScriptSearchConstants.DECLARATIONS : // cannot search for explicit member types - return new QualifiedTypeDeclarationPattern(qualificationChars, typeChars, indexSuffix, matchRule); - case IJavaScriptSearchConstants.REFERENCES : - return new TypeReferencePattern(qualificationChars, typeChars, typeSignature, matchRule); - case IJavaScriptSearchConstants.IMPLEMENTORS : - return new SuperTypeReferencePattern(qualificationChars, typeChars, matchRule); - case IJavaScriptSearchConstants.ALL_OCCURRENCES : - return new OrPattern( - new QualifiedTypeDeclarationPattern(qualificationChars, typeChars, indexSuffix, matchRule),// cannot search for explicit member types - new TypeReferencePattern(qualificationChars, typeChars, matchRule)); - } - return null; -} -/** - * Returns the enclosing type names of the given type. - */ -private static char[][] enclosingTypeNames(IType type) { - IJavaScriptElement parent = type.getParent(); - switch (parent.getElementType()) { - case IJavaScriptElement.CLASS_FILE: - // For a binary type, the parent is not the enclosing type, but the declaring type is. - // (see bug 20532 Declaration of member binary type not found) - IType declaringType = type.getDeclaringType(); - if (declaringType == null) return CharOperation.NO_CHAR_CHAR; - return CharOperation.arrayConcat( - enclosingTypeNames(declaringType), - declaringType.getElementName().toCharArray()); - case IJavaScriptElement.JAVASCRIPT_UNIT: - return CharOperation.NO_CHAR_CHAR; - case IJavaScriptElement.FIELD: - case IJavaScriptElement.INITIALIZER: - case IJavaScriptElement.METHOD: - IType declaringClass = ((IMember) parent).getDeclaringType(); - if (declaringClass!=null) - return CharOperation.arrayConcat( - enclosingTypeNames(declaringClass), - new char[][] {declaringClass.getElementName().toCharArray(), IIndexConstants.ONE_STAR}); - else - return CharOperation.NO_CHAR_CHAR; - case IJavaScriptElement.TYPE: - return CharOperation.arrayConcat( - enclosingTypeNames((IType)parent), - parent.getElementName().toCharArray()); - default: - return null; - } -} - -/** - * Decode the given index key in this pattern. The decoded index key is used by - * {@link #matchesDecodedKey(SearchPattern)} to find out if the corresponding index entry - * should be considered. - * <p> - * This method should be re-implemented in subclasses that need to decode an index key. - * </p> - * - * @param key the given index key - */ -public void decodeIndexKey(char[] key) { - // called from findIndexMatches(), override as necessary -} -/** - * Returns a blank pattern that can be used as a record to decode an index key. - * <p> - * Implementors of this method should return a new search pattern that is going to be used - * to decode index keys. - * </p> - * - * @return a new blank pattern - * @see #decodeIndexKey(char[]) - */ -public abstract SearchPattern getBlankPattern(); -/** - * Returns a key to find in relevant index categories, if null then all index entries are matched. - * The key will be matched according to some match rule. These potential matches - * will be further narrowed by the match locator, but precise match locating can be expensive, - * and index query should be as accurate as possible so as to eliminate obvious false hits. - * <p> - * This method should be re-implemented in subclasses that need to narrow down the - * index query. - * </p> - * - * @return an index key from this pattern, or <code>null</code> if all index entries are matched. - */ -public char[] getIndexKey() { - return null; // called from queryIn(), override as necessary -} -/** - * Returns an array of index categories to consider for this index query. - * These potential matches will be further narrowed by the match locator, but precise - * match locating can be expensive, and index query should be as accurate as possible - * so as to eliminate obvious false hits. - * <p> - * This method should be re-implemented in subclasses that need to narrow down the - * index query. - * </p> - * - * @return an array of index categories - */ -public char[][] getIndexCategories() { - return CharOperation.NO_CHAR_CHAR; // called from queryIn(), override as necessary -} -/** - * Returns the rule to apply for matching index keys. Can be exact match, prefix match, pattern match or regexp match. - * Rule can also be combined with a case sensitivity flag. - * - * @return one of R_EXACT_MATCH, R_PREFIX_MATCH, R_PATTERN_MATCH, R_REGEXP_MATCH combined with R_CASE_SENSITIVE, - * e.g. R_EXACT_MATCH | R_CASE_SENSITIVE if an exact and case sensitive match is requested, - * or R_PREFIX_MATCH if a prefix non case sensitive match is requested. - * [TODO (frederic) I hope R_ERASURE_MATCH doesn't need to be on this list. Because it would be a breaking API change.] - */ -public final int getMatchRule() { - return this.matchRule; -} -/** - * Returns whether this pattern matches the given pattern (representing a decoded index key). - * <p> - * This method should be re-implemented in subclasses that need to narrow down the - * index query. - * </p> - * - * @param decodedPattern a pattern representing a decoded index key - * @return whether this pattern matches the given pattern - */ -public boolean matchesDecodedKey(SearchPattern decodedPattern) { - return true; // called from findIndexMatches(), override as necessary if index key is encoded -} - -/** - * Returns whether the given name matches the given pattern. - * <p> - * This method should be re-implemented in subclasses that need to define how - * a name matches a pattern. - * </p> - * - * @param pattern the given pattern, or <code>null</code> to represent "*" - * @param name the given name - * @return whether the given name matches the given pattern - */ -public boolean matchesName(char[] pattern, char[] name) { - if (pattern == null) return true; // null is as if it was "*" - if (name != null) { - boolean isCaseSensitive = (this.matchRule & R_CASE_SENSITIVE) != 0; - boolean isCamelCase = (this.matchRule & R_CAMELCASE_MATCH) != 0; - int matchMode = this.matchRule & MODE_MASK; - boolean emptyPattern = pattern.length == 0; - if (matchMode == R_PREFIX_MATCH && emptyPattern) return true; - boolean sameLength = pattern.length == name.length; - boolean canBePrefix = name.length >= pattern.length; - boolean matchFirstChar = !isCaseSensitive || emptyPattern || (name.length > 0 && pattern[0] == name[0]); - if (isCamelCase && matchFirstChar && CharOperation.camelCaseMatch(pattern, name)) { - return true; - } - switch (matchMode) { - case R_EXACT_MATCH : - case R_FULL_MATCH : - if (!isCamelCase) { - if (sameLength && matchFirstChar) { - return CharOperation.equals(pattern, name, isCaseSensitive); - } - break; - } - // fall through next case to match as prefix if camel case failed - case R_PREFIX_MATCH : - if (canBePrefix && matchFirstChar) { - return CharOperation.prefixEquals(pattern, name, isCaseSensitive); - } - break; - - case R_PATTERN_MATCH : - if (!isCaseSensitive) - pattern = CharOperation.toLowerCase(pattern); - return CharOperation.match(pattern, name, isCaseSensitive); - - case R_REGEXP_MATCH : - // TODO (frederic) implement regular expression match - return true; - } - } - return false; -} - -/** - * Validate compatibility between given string pattern and match rule. - *<br> - * Optimized (ie. returned match rule is modified) combinations are: - * <ul> - * <li>{@link #R_PATTERN_MATCH} without any '*' or '?' in string pattern: - * pattern match bit is unset, - * </li> - * <li>{@link #R_PATTERN_MATCH} and {@link #R_PREFIX_MATCH} bits simultaneously set: - * prefix match bit is unset, - * </li> - * <li>{@link #R_PATTERN_MATCH} and {@link #R_CAMELCASE_MATCH} bits simultaneously set: - * camel case match bit is unset, - * </li> - * <li>{@link #R_CAMELCASE_MATCH} with invalid combination of uppercase and lowercase characters: - * camel case match bit is unset and replaced with prefix match pattern, - * </li> - * <li>{@link #R_CAMELCASE_MATCH} combined with {@link #R_PREFIX_MATCH} and {@link #R_CASE_SENSITIVE} - * bits is reduced to only {@link #R_CAMELCASE_MATCH} as Camel Case search is already prefix and case sensitive, - * </li> - * </ul> - *<br> - * Rejected (ie. returned match rule -1) combinations are: - * <ul> - * <li>{@link #R_REGEXP_MATCH} with any other match mode bit set, - * </li> - * </ul> - * - * @param stringPattern The string pattern - * @param matchRule The match rule - * @return Optimized valid match rule or -1 if an incompatibility was detected. - * - */ -public static int validateMatchRule(String stringPattern, int matchRule) { - - // Verify Regexp match rule - if ((matchRule & R_REGEXP_MATCH) != 0) { - if ((matchRule & R_PATTERN_MATCH) != 0 || (matchRule & R_PREFIX_MATCH) != 0 || (matchRule & R_CAMELCASE_MATCH) != 0) { - return -1; - } - } - - // Verify Pattern match rule - int starIndex = stringPattern.indexOf('*'); - int questionIndex = stringPattern.indexOf('?'); - if (starIndex < 0 && questionIndex < 0) { - // reset pattern match bit if any - matchRule &= ~R_PATTERN_MATCH; - } else { - // force Pattern rule - matchRule |= R_PATTERN_MATCH; - } - if ((matchRule & R_PATTERN_MATCH) != 0) { - // remove Camel Case and Prefix match bits if any - matchRule &= ~R_CAMELCASE_MATCH; - matchRule &= ~R_PREFIX_MATCH; - } - - // Verify Camel Case match rule - if ((matchRule & R_CAMELCASE_MATCH) != 0) { - // Verify sting pattern validity - int length = stringPattern.length(); - boolean validCamelCase = true; - boolean uppercase = false; - for (int i=0; i<length && validCamelCase; i++) { - char ch = stringPattern.charAt(i); - validCamelCase = ScannerHelper.isJavaIdentifierStart(ch); - // at least one uppercase character is need in CamelCase pattern - // (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=136313) - if (!uppercase) uppercase = ScannerHelper.isUpperCase(ch); - } - validCamelCase = validCamelCase && uppercase; - // Verify bits compatibility - if (validCamelCase) { - if ((matchRule & R_PREFIX_MATCH) != 0) { - if ((matchRule & R_CASE_SENSITIVE) != 0) { - // This is equivalent to Camel Case match rule - matchRule &= ~R_PREFIX_MATCH; - matchRule &= ~R_CASE_SENSITIVE; - } - } - } else { - matchRule &= ~R_CAMELCASE_MATCH; - if ((matchRule & R_PREFIX_MATCH) == 0) { - matchRule |= R_PREFIX_MATCH; - matchRule |= R_CASE_SENSITIVE; - } - } - } - return matchRule; -} - -/** - * @see java.lang.Object#toString() - */ -public String toString() { - return "SearchPattern"; //$NON-NLS-1$ -} -} +/*******************************************************************************
+ * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.jsdt.core.search;
+
+import java.util.regex.Pattern;
+
+import org.eclipse.wst.jsdt.core.IField;
+import org.eclipse.wst.jsdt.core.IFunction;
+import org.eclipse.wst.jsdt.core.IImportDeclaration;
+import org.eclipse.wst.jsdt.core.IJavaScriptElement;
+import org.eclipse.wst.jsdt.core.IMember;
+import org.eclipse.wst.jsdt.core.IType;
+import org.eclipse.wst.jsdt.core.JavaScriptModelException;
+import org.eclipse.wst.jsdt.core.Signature;
+import org.eclipse.wst.jsdt.core.compiler.CharOperation;
+import org.eclipse.wst.jsdt.core.compiler.InvalidInputException;
+import org.eclipse.wst.jsdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.wst.jsdt.internal.compiler.parser.Scanner;
+import org.eclipse.wst.jsdt.internal.compiler.parser.ScannerHelper;
+import org.eclipse.wst.jsdt.internal.compiler.parser.TerminalTokens;
+import org.eclipse.wst.jsdt.internal.core.LocalVariable;
+import org.eclipse.wst.jsdt.internal.core.search.indexing.IIndexConstants;
+import org.eclipse.wst.jsdt.internal.core.search.matching.ConstructorPattern;
+import org.eclipse.wst.jsdt.internal.core.search.matching.FieldPattern;
+import org.eclipse.wst.jsdt.internal.core.search.matching.InternalSearchPattern;
+import org.eclipse.wst.jsdt.internal.core.search.matching.LocalVariablePattern;
+import org.eclipse.wst.jsdt.internal.core.search.matching.MatchLocator;
+import org.eclipse.wst.jsdt.internal.core.search.matching.MethodPattern;
+import org.eclipse.wst.jsdt.internal.core.search.matching.OrPattern;
+import org.eclipse.wst.jsdt.internal.core.search.matching.PackageDeclarationPattern;
+import org.eclipse.wst.jsdt.internal.core.search.matching.PackageReferencePattern;
+import org.eclipse.wst.jsdt.internal.core.search.matching.SuperTypeReferencePattern;
+import org.eclipse.wst.jsdt.internal.core.search.matching.TypeDeclarationPattern;
+import org.eclipse.wst.jsdt.internal.core.search.matching.TypeReferencePattern;
+import org.eclipse.wst.jsdt.internal.core.util.QualificationHelpers;
+
+
+/**
+ * A search pattern defines how search results are found. Use <code>SearchPattern.createPattern</code>
+ * to create a search pattern.
+ * <p>
+ * Search patterns are used during the search phase to decode index entries that were added during the indexing phase
+ * (see {@link SearchDocument#addIndexEntry(char[], char[])}). When an index is queried, the
+ * index categories and keys to consider are retrieved from the search pattern using {@link #getIndexCategories()} and
+ * {@link #getIndexKey()}, as well as the match rule (see {@link #getMatchRule()}). A blank pattern is
+ * then created (see {@link #getBlankPattern()}). This blank pattern is used as a record as follows.
+ * For each index entry in the given index categories and that starts with the given key, the blank pattern is fed using
+ * {@link #decodeIndexKey(char[])}. The original pattern is then asked if it matches the decoded key using
+ * {@link #matchesDecodedKey(SearchPattern)}. If it matches, a search doument is created for this index entry
+ * using {@link SearchParticipant#getDocument(String)}.
+ *
+ * </p><p>
+ * This class is intended to be subclassed by clients. A default behavior is provided for each of the methods above, that
+ * clients can ovveride if they wish.
+ * </p>
+ * @see #createPattern(org.eclipse.wst.jsdt.core.IJavaScriptElement, int)
+ * @see #createPattern(String, int, int, int)
+ *
+ * Provisional API: This class/interface is part of an interim API that is still under development and expected to
+ * change significantly before reaching stability. It is being made available at this early stage to solicit feedback
+ * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
+ * (repeatedly) as the API evolves.
+ */
+public abstract class SearchPattern extends InternalSearchPattern {
+
+ // Rules for pattern matching: (exact, prefix, pattern) [ | case sensitive]
+ /**
+ * Match rule: The search pattern matches exactly the search result,
+ * that is, the source of the search result equals the search pattern.
+ */
+ public static final int R_EXACT_MATCH = 0;
+
+ /**
+ * Match rule: The search pattern is a prefix of the search result.
+ */
+ public static final int R_PREFIX_MATCH = 0x0001;
+
+ /**
+ * Match rule: The search pattern contains one or more wild cards ('*' or '?').
+ * A '*' wild-card can replace 0 or more characters in the search result.
+ * A '?' wild-card replaces exactly 1 character in the search result.
+ */
+ public static final int R_PATTERN_MATCH = 0x0002;
+
+ /**
+ * Match rule: The search pattern contains a regular expression.
+ */
+ public static final int R_REGEXP_MATCH = 0x0004;
+
+ /**
+ * Match rule: The search pattern matches the search result only if cases are the same.
+ * Can be combined to previous rules, e.g. {@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE}
+ */
+ public static final int R_CASE_SENSITIVE = 0x0008;
+
+ /**
+ * Match rule: The search pattern matches search results as raw/parameterized types/methods with same erasure.
+ * This mode has no effect on other javascript elements search.<br>
+ * Type search example:
+ * <ul>
+ * <li>pattern: <code>List<Exception></code></li>
+ * <li>match: <code>List<Object></code></li>
+ * </ul>
+ * Method search example:
+ * <ul>
+ * <li>declaration: <code><T>foo(T t)</code></li>
+ * <li>pattern: <code><Exception>foo(new Exception())</code></li>
+ * <li>match: <code><Object>foo(new Object())</code></li>
+ * </ul>
+ * Can be combined to all other match rules, e.g. {@link #R_CASE_SENSITIVE} | {@link #R_ERASURE_MATCH}
+ * This rule is not activated by default, so raw types or parameterized types with same erasure will not be found
+ * for pattern List<String>,
+ * Note that with this pattern, the match selection will be only on the erasure even for parameterized types.
+ *
+ */
+ public static final int R_ERASURE_MATCH = 0x0010;
+
+ /**
+ * Match rule: The search pattern matches search results as raw/parameterized types/methods with equivalent type parameters.
+ * This mode has no effect on other javascript elements search.<br>
+ * Type search example:
+ * <ul>
+ * <li>pattern: <code>List<Exception></code></li>
+ * <li>match:
+ * <ul>
+ * <li><code>List<? extends Throwable></code></li>
+ * <li><code>List<? super RuntimeException></code></li>
+ * <li><code>List<?></code></li>
+ * </ul>
+ * </li>
+ * </ul>
+ * Method search example:
+ * <ul>
+ * <li>declaration: <code><T>foo(T t)</code></li>
+ * <li>pattern: <code><Exception>foo(new Exception())</code></li>
+ * <li>match:
+ * <ul>
+ * <li><code><? extends Throwable>foo(new Exception())</code></li>
+ * <li><code><? super RuntimeException>foo(new Exception())</code></li>
+ * <li><code>foo(new Exception())</code></li>
+ * </ul>
+ * </ul>
+ * Can be combined to all other match rules, e.g. {@link #R_CASE_SENSITIVE} | {@link #R_EQUIVALENT_MATCH}
+ * This rule is not activated by default, so raw types or equivalent parameterized types will not be found
+ * for pattern List<String>,
+ * This mode is overridden by {@link #R_ERASURE_MATCH} as erasure matches obviously include equivalent ones.
+ * That means that pattern with rule set to {@link #R_EQUIVALENT_MATCH} | {@link #R_ERASURE_MATCH}
+ * will return same results than rule only set with {@link #R_ERASURE_MATCH}.
+ *
+ */
+ public static final int R_EQUIVALENT_MATCH = 0x0020;
+
+ /**
+ * Match rule: The search pattern matches exactly the search result,
+ * that is, the source of the search result equals the search pattern.
+ *
+ */
+ public static final int R_FULL_MATCH = 0x0040;
+
+ /**
+ * Match rule: The search pattern contains a Camel Case expression.
+ * <br>
+ * Examples:
+ * <ul>
+ * <li><code>NPE</code> type string pattern will match
+ * <code>NullPointerException</code> and <code>NpPermissionException</code> types,</li>
+ * <li><code>NuPoEx</code> type string pattern will only match
+ * <code>NullPointerException</code> type.</li>
+ * </ul>
+ * @see CharOperation#camelCaseMatch(char[], char[]) for a detailed explanation
+ * of Camel Case matching.
+ *<br>
+ * Can be combined to {@link #R_PREFIX_MATCH} match rule. For example,
+ * when prefix match rule is combined with Camel Case match rule,
+ * <code>"nPE"</code> pattern will match <code>nPException</code>.
+ *<br>
+ * Match rule {@link #R_PATTERN_MATCH} may also be combined but both rules
+ * will not be used simultaneously as they are mutually exclusive.
+ * Used match rule depends on whether string pattern contains specific pattern
+ * characters (e.g. '*' or '?') or not. If it does, then only Pattern match rule
+ * will be used, otherwise only Camel Case match will be used.
+ * For example, with <code>"NPE"</code> string pattern, search will only use
+ * Camel Case match rule, but with <code>N*P*E*</code> string pattern, it will
+ * use only Pattern match rule.
+ *
+ *
+ */
+ public static final int R_CAMELCASE_MATCH = 0x0080;
+
+ private static final int MODE_MASK = R_EXACT_MATCH | R_PREFIX_MATCH | R_PATTERN_MATCH | R_REGEXP_MATCH;
+
+ private int matchRule;
+
+/**
+ * Creates a search pattern with the rule to apply for matching index keys.
+ * It can be exact match, prefix match, pattern match or regexp match.
+ * Rule can also be combined with a case sensitivity flag.
+ *
+ * @param matchRule one of {@link #R_EXACT_MATCH}, {@link #R_PREFIX_MATCH}, {@link #R_PATTERN_MATCH},
+ * {@link #R_REGEXP_MATCH}, {@link #R_CAMELCASE_MATCH} combined with one of following values:
+ * {@link #R_CASE_SENSITIVE}, {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH}.
+ * e.g. {@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE} if an exact and case sensitive match is requested,
+ * {@link #R_PREFIX_MATCH} if a prefix non case sensitive match is requested or {@link #R_EXACT_MATCH} | {@link #R_ERASURE_MATCH}
+ * if a non case sensitive and erasure match is requested.<br>
+ * Note that {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH} have no effect
+ * on non-generic types/methods search.<br>
+ * Note also that default behavior for generic types/methods search is to find exact matches.
+ */
+public SearchPattern(int matchRule) {
+ this.matchRule = matchRule;
+ // Set full match implicit mode
+ if ((matchRule & (R_EQUIVALENT_MATCH | R_ERASURE_MATCH )) == 0) {
+ this.matchRule |= R_FULL_MATCH;
+ }
+}
+
+/**
+ * Answers true if the pattern matches the given name using CamelCase rules, or false otherwise.
+ * CamelCase matching does NOT accept explicit wild-cards '*' and '?' and is inherently case sensitive.
+ * <br>
+ * CamelCase denotes the convention of writing compound names without spaces, and capitalizing every term.
+ * This function recognizes both upper and lower CamelCase, depending whether the leading character is capitalized
+ * or not. The leading part of an upper CamelCase pattern is assumed to contain a sequence of capitals which are appearing
+ * in the matching name; e.g. 'NPE' will match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern
+ * uses a lowercase first character. In Java, type names follow the upper CamelCase convention, whereas method or field
+ * names follow the lower CamelCase convention.
+ * <br>
+ * The pattern may contain lowercase characters, which will be match in a case sensitive way. These characters must
+ * appear in sequence in the name. For instance, 'NPExcep' will match 'NullPointerException', but not 'NullPointerExCEPTION'
+ * or 'NuPoEx' will match 'NullPointerException', but not 'NoPointerException'.
+ * <br><br>
+ * Examples:
+ * <ol>
+ * <li><pre>
+ * pattern = "NPE"
+ * name = NullPointerException / NoPermissionException
+ * result => true
+ * </pre>
+ * </li>
+ * <li><pre>
+ * pattern = "NuPoEx"
+ * name = NullPointerException
+ * result => true
+ * </pre>
+ * </li>
+ * <li><pre>
+ * pattern = "npe"
+ * name = NullPointerException
+ * result => false
+ * </pre>
+ * </li>
+ * </ol>
+ * @see CharOperation#camelCaseMatch(char[], char[])
+ * Implementation has been entirely copied from this method except for array lengthes
+ * which were obviously replaced with calls to {@link String#length()}.
+ *
+ * @param pattern the given pattern
+ * @param name the given name
+ * @return true if the pattern matches the given name, false otherwise
+ *
+ */
+public static final boolean camelCaseMatch(String pattern, String name) {
+ if (pattern == null)
+ return true; // null pattern is equivalent to '*'
+ if (name == null)
+ return false; // null name cannot match
+
+ return camelCaseMatch(pattern, 0, pattern.length(), name, 0, name.length());
+}
+
+/**
+ * Answers true if a sub-pattern matches the subpart of the given name using CamelCase rules, or false otherwise.
+ * CamelCase matching does NOT accept explicit wild-cards '*' and '?' and is inherently case sensitive.
+ * Can match only subset of name/pattern, considering end positions as non-inclusive.
+ * The subpattern is defined by the patternStart and patternEnd positions.
+ * <br>
+ * CamelCase denotes the convention of writing compound names without spaces, and capitalizing every term.
+ * This function recognizes both upper and lower CamelCase, depending whether the leading character is capitalized
+ * or not. The leading part of an upper CamelCase pattern is assumed to contain a sequence of capitals which are appearing
+ * in the matching name; e.g. 'NPE' will match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern
+ * uses a lowercase first character. In Java, type names follow the upper CamelCase convention, whereas method or field
+ * names follow the lower CamelCase convention.
+ * <br>
+ * The pattern may contain lowercase characters, which will be match in a case sensitive way. These characters must
+ * appear in sequence in the name. For instance, 'NPExcep' will match 'NullPointerException', but not 'NullPointerExCEPTION'
+ * or 'NuPoEx' will match 'NullPointerException', but not 'NoPointerException'.
+ * <br><br>
+ * Examples:
+ * <ol>
+ * <li><pre>
+ * pattern = "NPE"
+ * patternStart = 0
+ * patternEnd = 3
+ * name = NullPointerException
+ * nameStart = 0
+ * nameEnd = 20
+ * result => true
+ * </pre>
+ * </li>
+ * <li><pre>
+ * pattern = "NPE"
+ * patternStart = 0
+ * patternEnd = 3
+ * name = NoPermissionException
+ * nameStart = 0
+ * nameEnd = 21
+ * result => true
+ * </pre>
+ * </li>
+ * <li><pre>
+ * pattern = "NuPoEx"
+ * patternStart = 0
+ * patternEnd = 6
+ * name = NullPointerException
+ * nameStart = 0
+ * nameEnd = 20
+ * result => true
+ * </pre>
+ * </li>
+ * <li><pre>
+ * pattern = "NuPoEx"
+ * patternStart = 0
+ * patternEnd = 6
+ * name = NoPermissionException
+ * nameStart = 0
+ * nameEnd = 21
+ * result => false
+ * </pre>
+ * </li>
+ * <li><pre>
+ * pattern = "npe"
+ * patternStart = 0
+ * patternEnd = 3
+ * name = NullPointerException
+ * nameStart = 0
+ * nameEnd = 20
+ * result => false
+ * </pre>
+ * </li>
+ * </ol>
+ * @see CharOperation#camelCaseMatch(char[], int, int, char[], int, int)
+ * Implementation has been entirely copied from this method except for array lengthes
+ * which were obviously replaced with calls to {@link String#length()} and
+ * for array direct access which were replaced with calls to {@link String#charAt(int)}.
+ *
+ * @param pattern the given pattern
+ * @param patternStart the start index of the pattern, inclusive
+ * @param patternEnd the end index of the pattern, exclusive
+ * @param name the given name
+ * @param nameStart the start index of the name, inclusive
+ * @param nameEnd the end index of the name, exclusive
+ * @return true if a sub-pattern matches the subpart of the given name, false otherwise
+ *
+ */
+public static final boolean camelCaseMatch(String pattern, int patternStart, int patternEnd, String name, int nameStart, int nameEnd) {
+ if (name == null)
+ return false; // null name cannot match
+ if (pattern == null)
+ return true; // null pattern is equivalent to '*'
+ if (patternEnd < 0) patternEnd = pattern.length();
+ if (nameEnd < 0) nameEnd = name.length();
+
+ if (patternEnd <= patternStart) return nameEnd <= nameStart;
+ if (nameEnd <= nameStart) return false;
+ // check first pattern char
+ if (name.charAt(nameStart) != pattern.charAt(patternStart)) {
+ // first char must strictly match (upper/lower)
+ return false;
+ }
+
+ char patternChar, nameChar;
+ int iPattern = patternStart;
+ int iName = nameStart;
+
+ // Main loop is on pattern characters
+ while (true) {
+
+ iPattern++;
+ iName++;
+
+ if (iPattern == patternEnd) {
+ // We have exhausted pattern, so it's a match
+ return true;
+ }
+
+ if (iName == nameEnd){
+ // We have exhausted name (and not pattern), so it's not a match
+ return false;
+ }
+
+ // For as long as we're exactly matching, bring it on (even if it's a lower case character)
+ if ((patternChar = pattern.charAt(iPattern)) == name.charAt(iName)) {
+ continue;
+ }
+
+ // If characters are not equals, then it's not a match if patternChar is lowercase
+ if (patternChar < ScannerHelper.MAX_OBVIOUS) {
+ if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[patternChar] & ScannerHelper.C_UPPER_LETTER) == 0) {
+ return false;
+ }
+ }
+ else if (Character.isJavaIdentifierPart(patternChar) && !Character.isUpperCase(patternChar)) {
+ return false;
+ }
+
+ // patternChar is uppercase, so let's find the next uppercase in name
+ while (true) {
+ if (iName == nameEnd){
+ // We have exhausted name (and not pattern), so it's not a match
+ return false;
+ }
+
+ nameChar = name.charAt(iName);
+
+ if (nameChar < ScannerHelper.MAX_OBVIOUS) {
+ if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[nameChar] & (ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_SPECIAL | ScannerHelper.C_DIGIT)) != 0) {
+ // nameChar is lowercase
+ iName++;
+ // nameChar is uppercase...
+ } else if (patternChar != nameChar) {
+ //.. and it does not match patternChar, so it's not a match
+ return false;
+ } else {
+ //.. and it matched patternChar. Back to the big loop
+ break;
+ }
+ }
+ else if (Character.isJavaIdentifierPart(nameChar) && !Character.isUpperCase(nameChar)) {
+ // nameChar is lowercase
+ iName++;
+ // nameChar is uppercase...
+ } else if (patternChar != nameChar) {
+ //.. and it does not match patternChar, so it's not a match
+ return false;
+ } else {
+ //.. and it matched patternChar. Back to the big loop
+ break;
+ }
+ }
+ // At this point, either name has been exhausted, or it is at an uppercase letter.
+ // Since pattern is also at an uppercase letter
+ }
+}
+
+/**
+ * Returns a search pattern that combines the given two patterns into an
+ * "and" pattern. The search result will match both the left pattern and
+ * the right pattern.
+ *
+ * @param leftPattern the left pattern
+ * @param rightPattern the right pattern
+ * @return an "and" pattern
+ */
+public static SearchPattern createAndPattern(SearchPattern leftPattern, SearchPattern rightPattern) {
+ return MatchLocator.createAndPattern(leftPattern, rightPattern);
+}
+
+/**
+ * Field pattern are formed by [declaringType.]name[ type]
+ * e.g. java.lang.String.serialVersionUID long
+ * field*
+ */
+private static SearchPattern createFieldPattern(String patternString, int limitTo, int matchRule,boolean isVar) {
+
+ Scanner scanner = new Scanner(false /*comment*/, true /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3/*sourceLevel*/, null /*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/);
+ scanner.setSource(patternString.toCharArray());
+ final int InsideDeclaringPart = 1;
+ final int InsideType = 2;
+ int lastToken = -1;
+
+ String declaringType = null, fieldName = null;
+ String type = null;
+ int mode = InsideDeclaringPart;
+ int token;
+ try {
+ token = scanner.getNextToken();
+ } catch (InvalidInputException e) {
+ return null;
+ }
+ while (token != TerminalTokens.TokenNameEOF) {
+ switch(mode) {
+ // read declaring type and fieldName
+ case InsideDeclaringPart :
+ switch (token) {
+ case TerminalTokens.TokenNameDOT:
+ if (declaringType == null) {
+ if (fieldName == null) return null;
+ declaringType = fieldName;
+ } else {
+ String tokenSource = scanner.getCurrentTokenString();
+ declaringType += tokenSource + fieldName;
+ }
+ fieldName = null;
+ break;
+ case TerminalTokens.TokenNameWHITESPACE:
+ if (!(TerminalTokens.TokenNameWHITESPACE == lastToken || TerminalTokens.TokenNameDOT == lastToken))
+ mode = InsideType;
+ break;
+ default: // all other tokens are considered identifiers (see bug 21763 Problem in JavaScript search [search])
+ if (fieldName == null)
+ fieldName = scanner.getCurrentTokenString();
+ else
+ fieldName += scanner.getCurrentTokenString();
+ }
+ break;
+ // read type
+ case InsideType:
+ switch (token) {
+ case TerminalTokens.TokenNameWHITESPACE:
+ break;
+ default: // all other tokens are considered identifiers (see bug 21763 Problem in JavaScript search [search])
+ if (type == null)
+ type = scanner.getCurrentTokenString();
+ else
+ type += scanner.getCurrentTokenString();
+ }
+ break;
+ }
+ lastToken = token;
+ try {
+ token = scanner.getNextToken();
+ } catch (InvalidInputException e) {
+ return null;
+ }
+ }
+ if (fieldName == null) return null;
+
+ char[] fieldNameChars = fieldName.toCharArray();
+ if (fieldNameChars.length == 1 && fieldNameChars[0] == '*') fieldNameChars = null;
+
+ char[] declaringTypeQualification = null;
+ char[] declaringTypeSimpleName = null;
+ char[] typeQualification = null;
+ char[] typeSimpleName = null;
+
+ // extract declaring type infos
+ if (declaringType != null) {
+ char[] declaringTypePart = declaringType.toCharArray();
+ int lastDotPosition = CharOperation.lastIndexOf('.', declaringTypePart);
+ if (lastDotPosition >= 0) {
+ declaringTypeQualification = CharOperation.subarray(declaringTypePart, 0, lastDotPosition);
+ if (declaringTypeQualification.length == 1 && declaringTypeQualification[0] == '*')
+ declaringTypeQualification = null;
+ declaringTypeSimpleName = CharOperation.subarray(declaringTypePart, lastDotPosition+1, declaringTypePart.length);
+ } else {
+ declaringTypeSimpleName = declaringTypePart;
+ }
+ if (declaringTypeSimpleName.length == 1 && declaringTypeSimpleName[0] == '*') {
+ declaringTypeSimpleName = null;
+ }
+ }
+ // extract type infos
+ if (type != null) {
+ char[] typePart = type.toCharArray();
+ int lastDotPosition = CharOperation.lastIndexOf('.', typePart);
+ if (lastDotPosition >= 0) {
+ typeQualification = CharOperation.subarray(typePart, 0, lastDotPosition);
+ if (typeQualification.length == 1 && typeQualification[0] == '*') {
+ typeQualification = null;
+ } else {
+ // prefix with a '*' as the full qualification could be bigger (because of an import)
+ typeQualification = CharOperation.concat(IIndexConstants.ONE_STAR, typeQualification);
+ }
+ typeSimpleName = CharOperation.subarray(typePart, lastDotPosition+1, typePart.length);
+ } else {
+ typeSimpleName = typePart;
+ }
+ if (typeSimpleName.length == 1 && typeSimpleName[0] == '*')
+ typeSimpleName = null;
+ }
+ // Create field pattern
+ boolean findDeclarations = false;
+ boolean readAccess = false;
+ boolean writeAccess = false;
+ switch (limitTo) {
+ case IJavaScriptSearchConstants.DECLARATIONS :
+ findDeclarations = true;
+ break;
+ case IJavaScriptSearchConstants.REFERENCES :
+ case IJavaScriptSearchConstants.READ_ACCESSES :
+ readAccess = true;
+ break;
+ case IJavaScriptSearchConstants.WRITE_ACCESSES :
+ writeAccess = true;
+ break;
+ case IJavaScriptSearchConstants.ALL_OCCURRENCES :
+ findDeclarations = true;
+ readAccess = true;
+ writeAccess = true;
+ break;
+ }
+ return new FieldPattern(
+ findDeclarations,
+ readAccess,
+ writeAccess,
+ isVar,
+ fieldNameChars,
+ declaringTypeQualification,
+ declaringTypeSimpleName,
+ typeQualification,
+ typeSimpleName,
+ matchRule,null);
+}
+
+/**
+ * Method pattern are formed by:<br>
+ * [declaringType '.'] ['<' typeArguments '>'] selector ['(' parameterTypes ')'] [returnType]
+ * <br>e.g.<ul>
+ * <li>java.lang.Runnable.run() void</li>
+ * <li>main(*)</li>
+ * <li><String>toArray(String[])</li>
+ * </ul>
+ * Constructor pattern are formed by:<br>
+ * [declaringQualification '.'] ['<' typeArguments '>'] type ['(' parameterTypes ')']
+ * <br>e.g.<ul>
+ * <li>java.lang.Object()</li>
+ * <li>Main(*)</li>
+ * <li><Exception>Sample(Exception)</li>
+ * </ul>
+ * Type arguments have the same pattern that for type patterns
+ * @see #createTypePattern(String,int,int,char)
+ */
+private static SearchPattern createMethodOrConstructorPattern(String patternString, int limitTo, int matchRule, boolean isConstructor, boolean isFunction) {
+
+ Scanner scanner = new Scanner(false /*comment*/, true /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3/*sourceLevel*/, null /*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/);
+ scanner.setSource(patternString.toCharArray());
+ final int InsideSelector = 1;
+ final int InsideTypeArguments = 2;
+ final int InsideParameter = 3;
+ final int InsideReturnType = 4;
+ int lastToken = -1;
+
+ String declaringType = null, selector = null, parameterType = null;
+ String[] parameterTypes = null;
+ String typeArgumentsString = null;
+ int parameterCount = -1;
+ String returnType = null;
+ boolean foundClosingParenthesis = false;
+ int mode = InsideSelector;
+ int token, argCount = 0;
+ try {
+ token = scanner.getNextToken();
+ } catch (InvalidInputException e) {
+ return null;
+ }
+ while (token != TerminalTokens.TokenNameEOF) {
+ switch(mode) {
+ // read declaring type and selector
+ case InsideSelector :
+ if (argCount == 0) {
+ switch (token) {
+ case TerminalTokens.TokenNameLESS:
+ argCount++;
+ if (selector == null || lastToken == TerminalTokens.TokenNameDOT) {
+ if (typeArgumentsString != null) return null; // invalid syntax
+ typeArgumentsString = scanner.getCurrentTokenString();
+ mode = InsideTypeArguments;
+ break;
+ }
+ if (declaringType == null) {
+ declaringType = selector;
+ } else {
+ declaringType += '.' + selector;
+ }
+ declaringType += scanner.getCurrentTokenString();
+ selector = null;
+ break;
+ case TerminalTokens.TokenNameDOT:
+ if (typeArgumentsString != null) return null; // invalid syntax
+ if (declaringType == null) {
+ if (selector == null) return null; // invalid syntax
+ declaringType = selector;
+ } else if (selector != null) {
+ declaringType += scanner.getCurrentTokenString() + selector;
+ }
+ selector = null;
+ break;
+ case TerminalTokens.TokenNameLPAREN:
+ parameterTypes = new String[5];
+ parameterCount = 0;
+ mode = InsideParameter;
+ break;
+ case TerminalTokens.TokenNameWHITESPACE:
+ switch (lastToken) {
+ case TerminalTokens.TokenNameWHITESPACE:
+ case TerminalTokens.TokenNameDOT:
+ case TerminalTokens.TokenNameGREATER:
+ case TerminalTokens.TokenNameRIGHT_SHIFT:
+ case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT:
+ break;
+ default:
+ mode = InsideReturnType;
+ break;
+ }
+ break;
+ default: // all other tokens are considered identifiers (see bug 21763 Problem in JavaScript search [search])
+ if (selector == null)
+ selector = scanner.getCurrentTokenString();
+ else
+ selector += scanner.getCurrentTokenString();
+ break;
+ }
+ } else {
+ if (declaringType == null) return null; // invalid syntax
+ switch (token) {
+ case TerminalTokens.TokenNameGREATER:
+ case TerminalTokens.TokenNameRIGHT_SHIFT:
+ case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT:
+ argCount--;
+ break;
+ case TerminalTokens.TokenNameLESS:
+ argCount++;
+ break;
+ }
+ declaringType += scanner.getCurrentTokenString();
+ }
+ break;
+ // read type arguments
+ case InsideTypeArguments:
+ if (typeArgumentsString == null) return null; // invalid syntax
+ typeArgumentsString += scanner.getCurrentTokenString();
+ switch (token) {
+ case TerminalTokens.TokenNameGREATER:
+ case TerminalTokens.TokenNameRIGHT_SHIFT:
+ case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT:
+ argCount--;
+ if (argCount == 0) {
+ mode = InsideSelector;
+ }
+ break;
+ case TerminalTokens.TokenNameLESS:
+ argCount++;
+ break;
+ }
+ break;
+ // read parameter types
+ case InsideParameter :
+ if (argCount == 0) {
+ switch (token) {
+ case TerminalTokens.TokenNameWHITESPACE:
+ break;
+ case TerminalTokens.TokenNameCOMMA:
+ if (parameterType == null) return null;
+ if (parameterTypes != null) {
+ if (parameterTypes.length == parameterCount)
+ System.arraycopy(parameterTypes, 0, parameterTypes = new String[parameterCount*2], 0, parameterCount);
+ parameterTypes[parameterCount++] = parameterType;
+ }
+ parameterType = null;
+ break;
+ case TerminalTokens.TokenNameRPAREN:
+ foundClosingParenthesis = true;
+ if (parameterType != null && parameterTypes != null) {
+ if (parameterTypes.length == parameterCount)
+ System.arraycopy(parameterTypes, 0, parameterTypes = new String[parameterCount*2], 0, parameterCount);
+ parameterTypes[parameterCount++] = parameterType;
+ }
+ mode = isConstructor ? InsideTypeArguments : InsideReturnType;
+ break;
+ case TerminalTokens.TokenNameLESS:
+ argCount++;
+ if (parameterType == null) return null; // invalid syntax
+ //$FALL-THROUGH$ next case to add token
+ default: // all other tokens are considered identifiers (see bug 21763 Problem in JavaScript search [search])
+ if (parameterType == null)
+ parameterType = scanner.getCurrentTokenString();
+ else
+ parameterType += scanner.getCurrentTokenString();
+ }
+ } else {
+ if (parameterType == null) return null; // invalid syntax
+ switch (token) {
+ case TerminalTokens.TokenNameGREATER:
+ case TerminalTokens.TokenNameRIGHT_SHIFT:
+ case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT:
+ argCount--;
+ break;
+ case TerminalTokens.TokenNameLESS:
+ argCount++;
+ break;
+ }
+ parameterType += scanner.getCurrentTokenString();
+ }
+ break;
+ // read return type
+ case InsideReturnType:
+ if (argCount == 0) {
+ switch (token) {
+ case TerminalTokens.TokenNameWHITESPACE:
+ break;
+ case TerminalTokens.TokenNameLPAREN:
+ parameterTypes = new String[5];
+ parameterCount = 0;
+ mode = InsideParameter;
+ break;
+ case TerminalTokens.TokenNameLESS:
+ argCount++;
+ if (returnType == null) return null; // invalid syntax
+ //$FALL-THROUGH$ next case to add token
+ default: // all other tokens are considered identifiers (see bug 21763 Problem in JavaScript search [search])
+ if (returnType == null)
+ returnType = scanner.getCurrentTokenString();
+ else
+ returnType += scanner.getCurrentTokenString();
+ }
+ } else {
+ if (returnType == null) return null; // invalid syntax
+ switch (token) {
+ case TerminalTokens.TokenNameGREATER:
+ case TerminalTokens.TokenNameRIGHT_SHIFT:
+ case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT:
+ argCount--;
+ break;
+ case TerminalTokens.TokenNameLESS:
+ argCount++;
+ break;
+ }
+ returnType += scanner.getCurrentTokenString();
+ }
+ break;
+ }
+ lastToken = token;
+ try {
+ token = scanner.getNextToken();
+ } catch (InvalidInputException e) {
+ return null;
+ }
+ }
+ // parenthesis mismatch
+ if (parameterCount>0 && !foundClosingParenthesis) return null;
+ // type arguments mismatch
+ if (argCount > 0) return null;
+
+ char[] selectorChars = null;
+ if (isConstructor) {
+ // retrieve type for constructor patterns
+ if (declaringType == null)
+ declaringType = selector;
+ else if (selector != null)
+ declaringType += '.' + selector;
+ } else {
+ // get selector chars
+ if (selector == null) return null;
+ selectorChars = selector.toCharArray();
+ if (selectorChars.length == 1 && selectorChars[0] == '*')
+ selectorChars = null;
+ }
+
+ char[] declaringTypeQualification = null, declaringTypeSimpleName = null;
+ char[] returnTypeQualification = null, returnTypeSimpleName = null;
+ char[][] parameterTypeQualifications = null, parameterTypeSimpleNames = null;
+ // Signatures
+ String[] parameterTypeSignatures = null;
+
+ // extract declaring type infos
+ if (declaringType != null) {
+ // get declaring type part and signature
+ char[] declaringTypePart = null;
+ try {
+ declaringTypePart = declaringType.toCharArray();
+ }
+ catch (IllegalArgumentException iae) {
+ // declaring type is invalid
+ return null;
+ }
+ int lastDotPosition = CharOperation.lastIndexOf('.', declaringTypePart);
+ if (lastDotPosition >= 0) {
+ declaringTypeQualification = CharOperation.subarray(declaringTypePart, 0, lastDotPosition);
+ if (declaringTypeQualification.length == 1 && declaringTypeQualification[0] == '*')
+ declaringTypeQualification = null;
+ declaringTypeSimpleName = CharOperation.subarray(declaringTypePart, lastDotPosition+1, declaringTypePart.length);
+ } else {
+ declaringTypeSimpleName = declaringTypePart;
+ }
+ if (declaringTypeSimpleName.length == 1 && declaringTypeSimpleName[0] == '*')
+ declaringTypeSimpleName = null;
+ }
+ // extract parameter types infos
+ if (parameterCount >= 0) {
+ parameterTypeQualifications = new char[parameterCount][];
+ parameterTypeSimpleNames = new char[parameterCount][];
+ parameterTypeSignatures = new String[parameterCount];
+ for (int i = 0; i < parameterCount; i++) {
+ // get parameter type part and signature
+ char[] parameterTypePart = null;
+ try {
+ if (parameterTypes != null) {
+ parameterTypeSignatures[i] = Signature.createTypeSignature(parameterTypes[i], false);
+ parameterTypePart = parameterTypes[i].toCharArray();
+ }
+ }
+ catch (IllegalArgumentException iae) {
+ // string is not a valid type syntax
+ return null;
+ }
+ int lastDotPosition = parameterTypePart==null ? -1 : CharOperation.lastIndexOf('.', parameterTypePart);
+ if (parameterTypePart != null && lastDotPosition >= 0) {
+ parameterTypeQualifications[i] = CharOperation.subarray(parameterTypePart, 0, lastDotPosition);
+ if (parameterTypeQualifications[i].length == 1 && parameterTypeQualifications[i][0] == '*') {
+ parameterTypeQualifications[i] = null;
+ } else {
+ // prefix with a '*' as the full qualification could be bigger (because of an import)
+ parameterTypeQualifications[i] = CharOperation.concat(IIndexConstants.ONE_STAR, parameterTypeQualifications[i]);
+ }
+ parameterTypeSimpleNames[i] = CharOperation.subarray(parameterTypePart, lastDotPosition+1, parameterTypePart.length);
+ } else {
+ parameterTypeQualifications[i] = null;
+ parameterTypeSimpleNames[i] = parameterTypePart;
+ }
+ if (parameterTypeSimpleNames[i].length == 1 && parameterTypeSimpleNames[i][0] == '*')
+ parameterTypeSimpleNames[i] = null;
+ }
+ }
+ // extract return type infos
+ if (returnType != null) {
+ // get return type part and signature
+ char[] returnTypePart = null;
+ try {
+ returnTypePart = returnType.toCharArray();
+ }
+ catch (IllegalArgumentException iae) {
+ // declaring type is invalid
+ return null;
+ }
+ int lastDotPosition = CharOperation.lastIndexOf('.', returnTypePart);
+ if (lastDotPosition >= 0) {
+ returnTypeQualification = CharOperation.subarray(returnTypePart, 0, lastDotPosition);
+ if (returnTypeQualification.length == 1 && returnTypeQualification[0] == '*') {
+ returnTypeQualification = null;
+ } else {
+ // because of an import
+ returnTypeQualification = CharOperation.concat(IIndexConstants.ONE_STAR, returnTypeQualification);
+ }
+ returnTypeSimpleName = CharOperation.subarray(returnTypePart, lastDotPosition+1, returnTypePart.length);
+ } else {
+ returnTypeSimpleName = returnTypePart;
+ }
+ if (returnTypeSimpleName.length == 1 && returnTypeSimpleName[0] == '*')
+ returnTypeSimpleName = null;
+ }
+ // Create method/constructor pattern
+ boolean findDeclarations = true;
+ boolean findReferences = true;
+ switch (limitTo) {
+ case IJavaScriptSearchConstants.DECLARATIONS :
+ findReferences = false;
+ break;
+ case IJavaScriptSearchConstants.REFERENCES :
+ findDeclarations = false;
+ break;
+ case IJavaScriptSearchConstants.ALL_OCCURRENCES :
+ break;
+ }
+ if (isConstructor) {
+ return new ConstructorPattern(patternString.toCharArray(), matchRule, findDeclarations, findReferences);
+ } else {
+ return new MethodPattern(
+ findDeclarations,
+ findReferences,
+ isFunction,
+ selectorChars,
+ parameterTypeQualifications,
+ parameterTypeSimpleNames,
+ returnTypeQualification,
+ returnTypeSimpleName,
+ declaringTypeQualification,
+ declaringTypeSimpleName,
+ matchRule);
+ }
+}
+
+/**
+ * Returns a search pattern that combines the given two patterns into an
+ * "or" pattern. The search result will match either the left pattern or the
+ * right pattern.
+ *
+ * @param leftPattern the left pattern
+ * @param rightPattern the right pattern
+ * @return an "or" pattern
+ */
+public static SearchPattern createOrPattern(SearchPattern leftPattern, SearchPattern rightPattern) {
+ return new OrPattern(leftPattern, rightPattern);
+}
+
+private static SearchPattern createPackagePattern(String patternString, int limitTo, int matchRule) {
+ switch (limitTo) {
+ case IJavaScriptSearchConstants.DECLARATIONS :
+ return new PackageDeclarationPattern(patternString.toCharArray(), matchRule);
+ case IJavaScriptSearchConstants.REFERENCES :
+ return new PackageReferencePattern(patternString.toCharArray(), matchRule);
+ case IJavaScriptSearchConstants.ALL_OCCURRENCES :
+ return new OrPattern(
+ new PackageDeclarationPattern(patternString.toCharArray(), matchRule),
+ new PackageReferencePattern(patternString.toCharArray(), matchRule)
+ );
+ }
+ return null;
+}
+
+/**
+ * Returns a search pattern based on a given string pattern. The string patterns support '*' wild-cards.
+ * The remaining parameters are used to narrow down the type of expected results.
+ *
+ * <br>
+ * Examples:
+ * <ul>
+ * <li>search for case insensitive references to <code>Object</code>:
+ * <code>createSearchPattern("Object", TYPE, REFERENCES, false);</code></li>
+ * <li>search for case sensitive references to exact <code>Object()</code> constructor:
+ * <code>createSearchPattern("java.lang.Object()", CONSTRUCTOR, REFERENCES, true);</code></li>
+ * <li>search for implementers of <code>java.lang.Runnable</code>:
+ * <code>createSearchPattern("java.lang.Runnable", TYPE, IMPLEMENTORS, true);</code></li>
+ * </ul>
+ * @param stringPattern the given pattern
+ * @param searchFor determines the nature of the searched elements
+ * <ul>
+ * <li>{@link IJavaScriptSearchConstants#CLASS}: only look for classes</li>
+ * <li>{@link IJavaScriptSearchConstants#INTERFACE}: only look for interfaces</li>
+ * <li>{@link IJavaScriptSearchConstants#ENUM}: only look for enumeration</li>
+ * <li>{@link IJavaScriptSearchConstants#ANNOTATION_TYPE}: only look for annotation type</li>
+ * <li>{@link IJavaScriptSearchConstants#CLASS_AND_ENUM}: only look for classes and enumerations</li>
+ * <li>{@link IJavaScriptSearchConstants#CLASS_AND_INTERFACE}: only look for classes and interfaces</li>
+ * <li>{@link IJavaScriptSearchConstants#TYPE}: look for all types (ie. classes, interfaces, enum and annotation types)</li>
+ * <li>{@link IJavaScriptSearchConstants#FIELD}: look for fields</li>
+ * <li>{@link IJavaScriptSearchConstants#METHOD}: look for methods</li>
+ * <li>{@link IJavaScriptSearchConstants#CONSTRUCTOR}: look for constructors</li>
+ * <li>{@link IJavaScriptSearchConstants#PACKAGE}: look for packages</li>
+ * </ul>
+ * @param limitTo determines the nature of the expected matches
+ * <ul>
+ * <li>{@link IJavaScriptSearchConstants#DECLARATIONS}: will search declarations matching
+ * with the corresponding element. In case the element is a method, declarations of matching
+ * methods in subtypes will also be found, allowing to find declarations of abstract methods, etc.<br>
+ * Note that additional flags {@link IJavaScriptSearchConstants#IGNORE_DECLARING_TYPE} and
+ * {@link IJavaScriptSearchConstants#IGNORE_RETURN_TYPE} are ignored for string patterns.
+ * This is due to the fact that client may omit to define them in string pattern to have same behavior.
+ * </li>
+ * <li>{@link IJavaScriptSearchConstants#REFERENCES}: will search references to the given element.</li>
+ * <li>{@link IJavaScriptSearchConstants#ALL_OCCURRENCES}: will search for either declarations or
+ * references as specified above.
+ * </li>
+ * <li>{@link IJavaScriptSearchConstants#IMPLEMENTORS}: for types, will find all types
+ * which directly implement/extend a given interface.
+ * Note that types may be only classes or only interfaces if {@link IJavaScriptSearchConstants#CLASS } or
+ * {@link IJavaScriptSearchConstants#INTERFACE} is respectively used instead of {@link IJavaScriptSearchConstants#TYPE}.
+ * </li>
+ * </ul>
+ * @param matchRule one of {@link #R_EXACT_MATCH}, {@link #R_PREFIX_MATCH}, {@link #R_PATTERN_MATCH},
+ * {@link #R_REGEXP_MATCH}, {@link #R_CAMELCASE_MATCH} combined with one of following values:
+ * {@link #R_CASE_SENSITIVE}, {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH}.
+ * e.g. {@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE} if an exact and case sensitive match is requested,
+ * {@link #R_PREFIX_MATCH} if a prefix non case sensitive match is requested or {@link #R_EXACT_MATCH} | {@link #R_ERASURE_MATCH}
+ * if a non case sensitive and erasure match is requested.<br>
+ * Note that {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH} have no effect
+ * on non-generic types/methods search.<br>
+ * Note also that default behavior for generic types/methods search is to find exact matches.
+ * @return a search pattern on the given string pattern, or <code>null</code> if the string pattern is ill-formed
+ */
+public static SearchPattern createPattern(String stringPattern, int searchFor, int limitTo, int matchRule) {
+ if (stringPattern == null || stringPattern.length() == 0) return null;
+
+ if ((matchRule = validateMatchRule(stringPattern, matchRule)) == -1) {
+ return null;
+ }
+
+ // Ignore additional nature flags
+ limitTo &= ~(IJavaScriptSearchConstants.IGNORE_DECLARING_TYPE+IJavaScriptSearchConstants.IGNORE_RETURN_TYPE);
+
+ switch (searchFor) {
+ case IJavaScriptSearchConstants.CLASS:
+ return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.CLASS_SUFFIX);
+ case IJavaScriptSearchConstants.TYPE:
+ return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.TYPE_SUFFIX);
+ case IJavaScriptSearchConstants.FUNCTION:
+ return createMethodOrConstructorPattern(stringPattern, limitTo, matchRule, false/*not a constructor*/,true);
+ case IJavaScriptSearchConstants.METHOD:
+ return createMethodOrConstructorPattern(stringPattern, limitTo, matchRule, false/*not a constructor*/,false);
+ case IJavaScriptSearchConstants.CONSTRUCTOR:
+ return createMethodOrConstructorPattern(stringPattern, limitTo, matchRule, true/*constructor*/,false);
+ case IJavaScriptSearchConstants.FIELD:
+ return createFieldPattern(stringPattern, limitTo, matchRule,false);
+ case IJavaScriptSearchConstants.VAR:
+ return createFieldPattern(stringPattern, limitTo, matchRule,true);
+ case IJavaScriptSearchConstants.PACKAGE:
+ return createPackagePattern(stringPattern, limitTo, matchRule);
+ }
+ return null;
+}
+
+/**
+ * Returns a search pattern based on a given JavaScript element.
+ * The pattern is used to trigger the appropriate search.
+ * <br>
+ * Note that for generic searches, the returned pattern consider {@link #R_ERASURE_MATCH} matches.
+ * If other kind of generic matches (ie. {@link #R_EXACT_MATCH} or {@link #R_EQUIVALENT_MATCH})
+ * are expected, {@link #createPattern(IJavaScriptElement, int, int)} method need to be used instead with
+ * the explicit match rule specified.
+ * <br>
+ * The pattern can be parameterized as follows:
+ *
+ * @param element the JavaScript element the search pattern is based on
+ * @param limitTo determines the nature of the expected matches
+ * <ul>
+ * <li>{@link IJavaScriptSearchConstants#DECLARATIONS}: will search declarations matching
+ * with the corresponding element. In case the element is a method, declarations of matching
+ * methods in subtypes will also be found, allowing to find declarations of abstract methods, etc.
+ * Some additional flags may be specified while searching declaration:
+ * <ul>
+ * <li>{@link IJavaScriptSearchConstants#IGNORE_DECLARING_TYPE}: declaring type will be ignored
+ * during the search.<br>
+ * For example using following test case:
+ * <pre>
+ * class A { A method() { return null; } }
+ * class B extends A { B method() { return null; } }
+ * class C { A method() { return null; } }
+ * </pre>
+ * search for <code>method</code> declaration with this flag
+ * will return 2 matches: in A and in C
+ * </li>
+ * <li>{@link IJavaScriptSearchConstants#IGNORE_RETURN_TYPE}: return type will be ignored
+ * during the search.<br>
+ * Using same example, search for <code>method</code> declaration with this flag
+ * will return 2 matches: in A and in B.
+ * </li>
+ * </ul>
+ * Note that these two flags may be combined and both declaring and return types can be ignored
+ * during the search. Then, using same example, search for <code>method</code> declaration
+ * with these 2 flags will return 3 matches: in A, in B and in C
+ * </li>
+ * <li>{@link IJavaScriptSearchConstants#REFERENCES}: will search references to the given element.</li>
+ * <li>{@link IJavaScriptSearchConstants#ALL_OCCURRENCES}: will search for either declarations or
+ * references as specified above.
+ * </li>
+ * <li>{@link IJavaScriptSearchConstants#IMPLEMENTORS}: for types, will find all types
+ * which directly implement/extend a given interface.
+ * </li>
+ * </ul>
+ * @return a search pattern for a JavaScript element or <code>null</code> if the given element is ill-formed
+ */
+public static SearchPattern createPattern(IJavaScriptElement element, int limitTo) {
+ return createPattern(element, limitTo, R_EXACT_MATCH | R_CASE_SENSITIVE | R_ERASURE_MATCH);
+}
+
+/**
+ * Returns a search pattern based on a given JavaScript element.
+ * The pattern is used to trigger the appropriate search, and can be parameterized as follows:
+ *
+ * @param element the JavaScript element the search pattern is based on
+ * @param limitTo determines the nature of the expected matches
+ * <ul>
+ * <li>{@link IJavaScriptSearchConstants#DECLARATIONS}: will search declarations matching
+ * with the corresponding element. In case the element is a method, declarations of matching
+ * methods in subtypes will also be found, allowing to find declarations of abstract methods, etc.
+ * Some additional flags may be specified while searching declaration:
+ * <ul>
+ * <li>{@link IJavaScriptSearchConstants#IGNORE_DECLARING_TYPE}: declaring type will be ignored
+ * during the search.<br>
+ * For example using following test case:
+ * <pre>
+ * class A { A method() { return null; } }
+ * class B extends A { B method() { return null; } }
+ * class C { A method() { return null; } }
+ * </pre>
+ * search for <code>method</code> declaration with this flag
+ * will return 2 matches: in A and in C
+ * </li>
+ * <li>{@link IJavaScriptSearchConstants#IGNORE_RETURN_TYPE}: return type will be ignored
+ * during the search.<br>
+ * Using same example, search for <code>method</code> declaration with this flag
+ * will return 2 matches: in A and in B.
+ * </li>
+ * </ul>
+ * Note that these two flags may be combined and both declaring and return types can be ignored
+ * during the search. Then, using same example, search for <code>method</code> declaration
+ * with these 2 flags will return 3 matches: in A, in B and in C
+ * </li>
+ * <li>{@link IJavaScriptSearchConstants#REFERENCES}: will search references to the given element.</li>
+ * <li>{@link IJavaScriptSearchConstants#ALL_OCCURRENCES}: will search for either declarations or
+ * references as specified above.
+ * </li>
+ * <li>{@link IJavaScriptSearchConstants#IMPLEMENTORS}: for types, will find all types
+ * which directly implement/extend a given interface.
+ * </li>
+ * </ul>
+ * @param matchRule one of {@link #R_EXACT_MATCH}, {@link #R_PREFIX_MATCH}, {@link #R_PATTERN_MATCH},
+ * {@link #R_REGEXP_MATCH}, {@link #R_CAMELCASE_MATCH} combined with one of following values:
+ * {@link #R_CASE_SENSITIVE}, {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH}.
+ * e.g. {@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE} if an exact and case sensitive match is requested,
+ * {@link #R_PREFIX_MATCH} if a prefix non case sensitive match is requested or {@link #R_EXACT_MATCH} |{@link #R_ERASURE_MATCH}
+ * if a non case sensitive and erasure match is requested.<br>
+ * Note that {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH} have no effect on non-generic types
+ * or methods search.<br>
+ * Note also that default behavior for generic types or methods is to find exact matches.
+ * @return a search pattern for a JavaScript element or <code>null</code> if the given element is ill-formed
+ *
+ */
+public static SearchPattern createPattern(IJavaScriptElement element, int limitTo, int matchRule) {
+ SearchPattern searchPattern = null;
+ int lastDot;
+ boolean ignoreDeclaringType = false;
+ boolean ignoreReturnType = false;
+ int maskedLimitTo = limitTo & ~(IJavaScriptSearchConstants.IGNORE_DECLARING_TYPE+IJavaScriptSearchConstants.IGNORE_RETURN_TYPE);
+ if (maskedLimitTo == IJavaScriptSearchConstants.DECLARATIONS || maskedLimitTo == IJavaScriptSearchConstants.ALL_OCCURRENCES) {
+ ignoreDeclaringType = (limitTo & IJavaScriptSearchConstants.IGNORE_DECLARING_TYPE) != 0;
+ ignoreReturnType = (limitTo & IJavaScriptSearchConstants.IGNORE_RETURN_TYPE) != 0;
+ }
+ char[] declaringSimpleName = null;
+ char[] declaringQualification = null;
+ boolean isVar=false;
+ boolean isFunction=false;
+ switch (element.getElementType()) {
+ case IJavaScriptElement.FIELD :
+ IField field = (IField) element;
+ IType declaringClassForField = field.getDeclaringType();
+ isVar=(declaringClassForField==null);
+ if (!ignoreDeclaringType) {
+ if (declaringClassForField!=null)
+ {
+ declaringSimpleName = declaringClassForField.getElementName().toCharArray();
+ declaringQualification= declaringClassForField.getPackageFragment().getElementName().toCharArray();
+ char[][] enclosingNames = enclosingTypeNames(declaringClassForField);
+ if (enclosingNames.length > 0) {
+ declaringQualification = CharOperation.concat(declaringQualification, CharOperation.concatWith(enclosingNames, '.'), '.');
+ }
+ }
+ }
+ char[] name = field.getElementName().toCharArray();
+ char[] typeSimpleName = null;
+ char[] typeQualification = null;
+ String typeSignature = null;
+ if (!ignoreReturnType) {
+ try {
+ typeSignature = field.getTypeSignature();
+ char[] signature = typeSignature.toCharArray();
+ char[] typeErasure = Signature.toCharArray(signature);
+ if ((lastDot = CharOperation.lastIndexOf('.', typeErasure)) == -1) {
+ typeSimpleName = typeErasure;
+ } else {
+ typeSimpleName = CharOperation.subarray(typeErasure, lastDot + 1, typeErasure.length);
+ typeQualification = CharOperation.subarray(typeErasure, 0, lastDot);
+ if (!field.isBinary()) {
+ // prefix with a '*' as the full qualification could be bigger (because of an import)
+ typeQualification = CharOperation.concat(IIndexConstants.ONE_STAR, typeQualification);
+ }
+ }
+ } catch (JavaScriptModelException e) {
+ return null;
+ }
+ }
+
+ // Create field pattern
+ boolean findDeclarations = false;
+ boolean readAccess = false;
+ boolean writeAccess = false;
+ switch (maskedLimitTo) {
+ case IJavaScriptSearchConstants.DECLARATIONS :
+ findDeclarations = true;
+ break;
+ case IJavaScriptSearchConstants.REFERENCES :
+ case IJavaScriptSearchConstants.READ_ACCESSES :
+ readAccess = true;
+ break;
+ case IJavaScriptSearchConstants.WRITE_ACCESSES :
+ writeAccess = true;
+ break;
+ case IJavaScriptSearchConstants.ALL_OCCURRENCES :
+ findDeclarations = true;
+ readAccess = true;
+ writeAccess = true;
+ break;
+ }
+ searchPattern =
+ new FieldPattern(
+ findDeclarations,
+ readAccess,
+ writeAccess,
+ isVar,
+ name,
+ declaringQualification,
+ declaringSimpleName,
+ typeQualification,
+ typeSimpleName,
+ matchRule,field);
+ break;
+ case IJavaScriptElement.IMPORT_DECLARATION :
+ String elementName = element.getElementName();
+ lastDot = elementName.lastIndexOf('.');
+ if (lastDot == -1) return null; // invalid import declaration
+ IImportDeclaration importDecl = (IImportDeclaration)element;
+ if (importDecl.isOnDemand()) {
+ searchPattern = createPackagePattern(elementName.substring(0, lastDot), maskedLimitTo, matchRule);
+ } else {
+ searchPattern =
+ createTypePattern(
+ elementName.substring(lastDot+1).toCharArray(),
+ elementName.substring(0, lastDot).toCharArray(),
+ null,
+ null,
+ null,
+ maskedLimitTo,
+ matchRule);
+ }
+ break;
+ case IJavaScriptElement.LOCAL_VARIABLE :
+ LocalVariable localVar = (LocalVariable) element;
+ boolean findVarDeclarations = false;
+ boolean findVarReadAccess = false;
+ boolean findVarWriteAccess = false;
+ switch (maskedLimitTo) {
+ case IJavaScriptSearchConstants.DECLARATIONS :
+ findVarDeclarations = true;
+ break;
+ case IJavaScriptSearchConstants.REFERENCES :
+ findVarReadAccess = true;
+ findVarWriteAccess = true;
+ break;
+ case IJavaScriptSearchConstants.READ_ACCESSES :
+ findVarReadAccess = true;
+ break;
+ case IJavaScriptSearchConstants.WRITE_ACCESSES :
+ findVarWriteAccess = true;
+ break;
+ case IJavaScriptSearchConstants.ALL_OCCURRENCES :
+ findVarDeclarations = true;
+ findVarReadAccess = true;
+ findVarWriteAccess = true;
+ break;
+ }
+ searchPattern =
+ new LocalVariablePattern(
+ findVarDeclarations,
+ findVarReadAccess,
+ findVarWriteAccess,
+ localVar,
+ matchRule);
+ break;
+ case IJavaScriptElement.METHOD :
+ IFunction method = (IFunction) element;
+ boolean isConstructor;
+ try {
+ isConstructor = method.isConstructor();
+ } catch (JavaScriptModelException e) {
+ return null;
+ }
+ IType declaringClass = method.getDeclaringType();
+
+ if (declaringClass!=null) {
+ if (ignoreDeclaringType) {
+ if (isConstructor)
+ declaringSimpleName = declaringClass.getElementName().toCharArray();
+ } else {
+ declaringSimpleName = declaringClass.getElementName().toCharArray();
+ declaringQualification = declaringClass.getPackageFragment().getElementName().toCharArray();
+ char[][] enclosingNames = enclosingTypeNames(declaringClass);
+ if (enclosingNames.length > 0) {
+ declaringQualification = CharOperation.concat(
+ declaringQualification, CharOperation.concatWith(enclosingNames, '.'), '.');
+ }
+ }
+ }
+ else
+ isFunction=true;
+ char[] selector = method.getElementName().toCharArray();
+ char[] returnSimpleName = null;
+ char[] returnQualification = null;
+ String returnSignature = null;
+ if (!ignoreReturnType) {
+ try {
+ returnSignature = method.getReturnType();
+ char[] signature = returnSignature.toCharArray();
+ char[] returnErasure = Signature.toCharArray(signature);
+ if ((lastDot = CharOperation.lastIndexOf('.', returnErasure)) == -1) {
+ returnSimpleName = returnErasure;
+ } else {
+ returnSimpleName = CharOperation.subarray(returnErasure, lastDot + 1, returnErasure.length);
+ returnQualification = CharOperation.subarray(returnErasure, 0, lastDot);
+ if (!method.isBinary()) {
+ // prefix with a '*' as the full qualification could be bigger (because of an import)
+ CharOperation.concat(IIndexConstants.ONE_STAR, returnQualification);
+ }
+ }
+ } catch (JavaScriptModelException e) {
+ return null;
+ }
+ }
+ String[] parameterTypes = method.getParameterTypes();
+ int paramCount = parameterTypes.length;
+ char[][] parameterSimpleNames = new char[paramCount][];
+ char[][] parameterQualifications = new char[paramCount][];
+ String[] parameterSignatures = new String[paramCount];
+ for (int i = 0; i < paramCount; i++) {
+ parameterSignatures[i] = parameterTypes[i];
+ char[] signature = parameterSignatures[i].toCharArray();
+ char[] paramErasure = Signature.toCharArray(signature);
+ if ((lastDot = CharOperation.lastIndexOf('.', paramErasure)) == -1) {
+ parameterSimpleNames[i] = paramErasure;
+ parameterQualifications[i] = null;
+ } else {
+ parameterSimpleNames[i] = CharOperation.subarray(paramErasure, lastDot + 1, paramErasure.length);
+ parameterQualifications[i] = CharOperation.subarray(paramErasure, 0, lastDot);
+ if (!method.isBinary()) {
+ // prefix with a '*' as the full qualification could be bigger (because of an import)
+ CharOperation.concat(IIndexConstants.ONE_STAR, parameterQualifications[i]);
+ }
+ }
+ }
+
+ // Create method/constructor pattern
+ boolean findMethodDeclarations = true;
+ boolean findMethodReferences = true;
+ switch (maskedLimitTo) {
+ case IJavaScriptSearchConstants.DECLARATIONS :
+ findMethodReferences = false;
+ break;
+ case IJavaScriptSearchConstants.REFERENCES :
+ findMethodDeclarations = false;
+ break;
+ case IJavaScriptSearchConstants.ALL_OCCURRENCES :
+ break;
+ }
+ if (isConstructor) {
+ searchPattern =
+ new ConstructorPattern(
+ findMethodDeclarations,
+ findMethodReferences,
+ parameterQualifications,
+ parameterSimpleNames,
+ declaringSimpleName,
+ declaringQualification,
+ matchRule);
+ } else {
+ searchPattern =
+ new MethodPattern(
+ findMethodDeclarations,
+ findMethodReferences,
+ isFunction,
+ selector,
+ parameterQualifications,
+ parameterSimpleNames,
+ returnQualification,
+ returnSimpleName,
+ declaringQualification,
+ declaringSimpleName,
+ matchRule);
+ }
+ break;
+ case IJavaScriptElement.TYPE :
+ IType type = (IType)element;
+ searchPattern = createTypePattern(
+ type.getElementName().toCharArray(),
+ type.getPackageFragment().getElementName().toCharArray(),
+ ignoreDeclaringType ? null : enclosingTypeNames(type),
+ null,
+ type,
+ maskedLimitTo,
+ matchRule);
+ break;
+ case IJavaScriptElement.PACKAGE_FRAGMENT :
+ searchPattern = createPackagePattern(element.getElementName(), maskedLimitTo, matchRule);
+ break;
+ }
+ if (searchPattern != null)
+ MatchLocator.setFocus(searchPattern, element);
+ return searchPattern;
+}
+
+private static SearchPattern createTypePattern(char[] simpleName, char[] packageName, char[][] enclosingTypeNames, String typeSignature, IType type, int limitTo, int matchRule) {
+ switch (limitTo) {
+ case IJavaScriptSearchConstants.DECLARATIONS :
+ return new TypeDeclarationPattern(
+ packageName,
+ simpleName,
+ enclosingTypeNames,
+ matchRule);
+ case IJavaScriptSearchConstants.REFERENCES :
+ if (type != null) {
+ return new TypeReferencePattern(
+ CharOperation.concatWith(packageName, enclosingTypeNames, '.'),
+ simpleName,
+ type,
+ matchRule);
+ }
+ return new TypeReferencePattern(
+ CharOperation.concatWith(packageName, enclosingTypeNames, '.'),
+ simpleName,
+ typeSignature,
+ matchRule);
+ case IJavaScriptSearchConstants.IMPLEMENTORS :
+ return new SuperTypeReferencePattern(
+ QualificationHelpers.createFullyQualifiedName(packageName, simpleName),
+ matchRule);
+ case IJavaScriptSearchConstants.ALL_OCCURRENCES :
+ return new OrPattern(
+ new TypeDeclarationPattern(
+ packageName,
+ simpleName,
+ enclosingTypeNames,
+ matchRule),
+ (type != null)
+ ? new TypeReferencePattern(
+ CharOperation.concatWith(packageName, enclosingTypeNames, '.'),
+ simpleName,
+ type,
+ matchRule)
+ : new TypeReferencePattern(
+ CharOperation.concatWith(packageName, enclosingTypeNames, '.'),
+ simpleName,
+ typeSignature,
+ matchRule)
+ );
+ }
+ return null;
+}
+/**
+ * Type pattern are formed by [qualification '.']type [typeArguments].
+ * e.g. java.lang.Object
+ * Runnable
+ * List<String>
+ */
+private static SearchPattern createTypePattern(String patternString, int limitTo, int matchRule, char indexSuffix) {
+
+ Scanner scanner = new Scanner(false /*comment*/, true /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3/*sourceLevel*/, null /*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/);
+ scanner.setSource(patternString.toCharArray());
+ String type = null;
+ int token;
+ try {
+ token = scanner.getNextToken();
+ } catch (InvalidInputException e) {
+ return null;
+ }
+ int argCount = 0;
+ while (token != TerminalTokens.TokenNameEOF) {
+ if (argCount == 0) {
+ switch (token) {
+ case TerminalTokens.TokenNameWHITESPACE:
+ break;
+ case TerminalTokens.TokenNameLESS:
+ argCount++;
+ // fall through default case to add token to type
+ default: // all other tokens are considered identifiers (see bug 21763 Problem in JavaScript search [search])
+ if (type == null)
+ type = scanner.getCurrentTokenString();
+ else
+ type += scanner.getCurrentTokenString();
+ }
+ } else {
+ switch (token) {
+ case TerminalTokens.TokenNameGREATER:
+ case TerminalTokens.TokenNameRIGHT_SHIFT:
+ case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT:
+ argCount--;
+ break;
+ case TerminalTokens.TokenNameLESS:
+ argCount++;
+ break;
+ }
+ if (type == null) return null; // invalid syntax
+ type += scanner.getCurrentTokenString();
+ }
+ try {
+ token = scanner.getNextToken();
+ } catch (InvalidInputException e) {
+ return null;
+ }
+ }
+ if (type == null) return null;
+ String typeSignature = null;
+ char[] qualificationChars = null, typeChars = null;
+
+ // get type part and signature
+ char[] typePart = null;
+ try {
+ typeSignature = Signature.createTypeSignature(type, false);
+ typePart = type.toCharArray();
+ }
+ catch (IllegalArgumentException iae) {
+ // string is not a valid type syntax
+ return null;
+ }
+
+ // get qualification name
+ int lastDotPosition = CharOperation.lastIndexOf('.', typePart);
+ if (lastDotPosition >= 0) {
+ qualificationChars = CharOperation.subarray(typePart, 0, lastDotPosition);
+ if (qualificationChars.length == 1 && qualificationChars[0] == '*')
+ qualificationChars = null;
+ typeChars = CharOperation.subarray(typePart, lastDotPosition+1, typePart.length);
+ } else {
+ typeChars = typePart;
+ }
+ if (typeChars.length == 1 && typeChars[0] == '*') {
+ typeChars = null;
+ }
+ switch (limitTo) {
+ case IJavaScriptSearchConstants.DECLARATIONS : // cannot search for explicit member types
+ return new TypeDeclarationPattern(typePart, matchRule);
+ case IJavaScriptSearchConstants.REFERENCES :
+ return new TypeReferencePattern(qualificationChars, typeChars, typeSignature, matchRule);
+ case IJavaScriptSearchConstants.IMPLEMENTORS :
+ return new SuperTypeReferencePattern(QualificationHelpers.createFullyQualifiedName(qualificationChars, typeChars), matchRule);
+ case IJavaScriptSearchConstants.ALL_OCCURRENCES :
+ return new OrPattern(
+ new TypeDeclarationPattern(typePart, matchRule),// cannot search for explicit member types
+ new TypeReferencePattern(qualificationChars, typeChars, matchRule));
+ }
+ return null;
+}
+/**
+ * Returns the enclosing type names of the given type.
+ */
+private static char[][] enclosingTypeNames(IType type) {
+ IJavaScriptElement parent = type.getParent();
+ switch (parent.getElementType()) {
+ case IJavaScriptElement.CLASS_FILE:
+ // For a binary type, the parent is not the enclosing type, but the declaring type is.
+ // (see bug 20532 Declaration of member binary type not found)
+ IType declaringType = type.getDeclaringType();
+ if (declaringType == null) return CharOperation.NO_CHAR_CHAR;
+ return CharOperation.arrayConcat(
+ enclosingTypeNames(declaringType),
+ declaringType.getElementName().toCharArray());
+ case IJavaScriptElement.JAVASCRIPT_UNIT:
+ return CharOperation.NO_CHAR_CHAR;
+ case IJavaScriptElement.FIELD:
+ case IJavaScriptElement.INITIALIZER:
+ case IJavaScriptElement.METHOD:
+ IType declaringClass = ((IMember) parent).getDeclaringType();
+ if (declaringClass!=null)
+ return CharOperation.arrayConcat(
+ enclosingTypeNames(declaringClass),
+ new char[][] {declaringClass.getElementName().toCharArray(), IIndexConstants.ONE_STAR});
+ else
+ return CharOperation.NO_CHAR_CHAR;
+ case IJavaScriptElement.TYPE:
+ return CharOperation.arrayConcat(
+ enclosingTypeNames((IType)parent),
+ parent.getElementName().toCharArray());
+ default:
+ return null;
+ }
+}
+
+/**
+ * Decode the given index key in this pattern. The decoded index key is used by
+ * {@link #matchesDecodedKey(SearchPattern)} to find out if the corresponding index entry
+ * should be considered.
+ * <p>
+ * This method should be re-implemented in subclasses that need to decode an index key.
+ * </p>
+ *
+ * @param key the given index key
+ */
+public void decodeIndexKey(char[] key) {
+ // called from findIndexMatches(), override as necessary
+}
+/**
+ * Returns a blank pattern that can be used as a record to decode an index key.
+ * <p>
+ * Implementors of this method should return a new search pattern that is going to be used
+ * to decode index keys.
+ * </p>
+ *
+ * @return a new blank pattern
+ * @see #decodeIndexKey(char[])
+ */
+public abstract SearchPattern getBlankPattern();
+/**
+ * Returns a key to find in relevant index categories, if null then all index entries are matched.
+ * The key will be matched according to some match rule. These potential matches
+ * will be further narrowed by the match locator, but precise match locating can be expensive,
+ * and index query should be as accurate as possible so as to eliminate obvious false hits.
+ * <p>
+ * This method should be re-implemented in subclasses that need to narrow down the
+ * index query.
+ * </p>
+ *
+ * @return an index key from this pattern, or <code>null</code> if all index entries are matched.
+ */
+public char[] getIndexKey() {
+ return null; // called from queryIn(), override as necessary
+}
+/**
+ * Returns an array of index categories to consider for this index query.
+ * These potential matches will be further narrowed by the match locator, but precise
+ * match locating can be expensive, and index query should be as accurate as possible
+ * so as to eliminate obvious false hits.
+ * <p>
+ * This method should be re-implemented in subclasses that need to narrow down the
+ * index query.
+ * </p>
+ *
+ * @return an array of index categories
+ */
+public char[][] getIndexCategories() {
+ return CharOperation.NO_CHAR_CHAR; // called from queryIn(), override as necessary
+}
+/**
+ * Returns the rule to apply for matching index keys. Can be exact match, prefix match, pattern match or regexp match.
+ * Rule can also be combined with a case sensitivity flag.
+ *
+ * @return one of R_EXACT_MATCH, R_PREFIX_MATCH, R_PATTERN_MATCH, R_REGEXP_MATCH combined with R_CASE_SENSITIVE,
+ * e.g. R_EXACT_MATCH | R_CASE_SENSITIVE if an exact and case sensitive match is requested,
+ * or R_PREFIX_MATCH if a prefix non case sensitive match is requested.
+ */
+public final int getMatchRule() {
+ return this.matchRule;
+}
+/**
+ * Returns whether this pattern matches the given pattern (representing a decoded index key).
+ * <p>
+ * This method should be re-implemented in subclasses that need to narrow down the
+ * index query.
+ * </p>
+ *
+ * @param decodedPattern a pattern representing a decoded index key
+ * @return whether this pattern matches the given pattern
+ */
+public boolean matchesDecodedKey(SearchPattern decodedPattern) {
+ return true; // called from findIndexMatches(), override as necessary if index key is encoded
+}
+
+/**
+ * Returns whether the given name matches the given pattern.
+ * <p>
+ * This method should be re-implemented in subclasses that need to define how
+ * a name matches a pattern.
+ * </p>
+ *
+ * @param pattern the given pattern, or <code>null</code> to represent "*"
+ * @param name the given name
+ * @return whether the given name matches the given pattern
+ */
+public boolean matchesName(char[] pattern, char[] name) {
+ if (pattern == null) return true; // null is as if it was "*"
+ if (name != null) {
+ boolean isCaseSensitive = (this.matchRule & R_CASE_SENSITIVE) != 0;
+ boolean isCamelCase = (this.matchRule & R_CAMELCASE_MATCH) != 0;
+ int matchMode = this.matchRule & MODE_MASK;
+ boolean emptyPattern = pattern.length == 0;
+ if (matchMode == R_PREFIX_MATCH && emptyPattern) return true;
+ boolean sameLength = pattern.length == name.length;
+ boolean canBePrefix = name.length >= pattern.length;
+ boolean matchFirstChar = !isCaseSensitive || emptyPattern || (name.length > 0 && pattern[0] == name[0]);
+ if (isCamelCase && matchFirstChar && CharOperation.camelCaseMatch(pattern, name)) {
+ return true;
+ }
+ switch (matchMode) {
+ case R_EXACT_MATCH :
+ case R_FULL_MATCH :
+ if (!isCamelCase) {
+ if (sameLength && matchFirstChar) {
+ return CharOperation.equals(pattern, name, isCaseSensitive);
+ }
+ break;
+ }
+ //$FALL-THROUGH$ next case to match as prefix if camel case failed
+ case R_PREFIX_MATCH :
+ if (canBePrefix && matchFirstChar) {
+ return CharOperation.prefixEquals(pattern, name, isCaseSensitive);
+ }
+ break;
+
+ case R_PATTERN_MATCH :
+ if (!isCaseSensitive)
+ pattern = CharOperation.toLowerCase(pattern);
+ return CharOperation.match(pattern, name, isCaseSensitive);
+
+ case R_REGEXP_MATCH : {
+ int flags = 0;
+ if(!isCaseSensitive) {
+ flags = Pattern.CASE_INSENSITIVE;
+ }
+ return Pattern.compile(new String(pattern), flags).matcher(new String(name)).matches();
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * <p>Returns whether this pattern's prefix qualifier matches the other patterns qualified name.
+ * This pattern's qualifier must have a match rule of pattern match for a match to occur.</p>
+ *
+ * @param thisQualifiedName this qualifier pattern to inspect
+ * @param otherQualifiedName other qualifier pattern to inspect
+ * @param isCaseSensitive whether this qualifier pattern is case sensitive
+ * @return whether this pattern's prefix qualifier matches the other patterns qualified name
+ */
+public static boolean matchesQualificationPattern(char[] thisQualifiedName, char[] otherQualifiedName, boolean isCaseSensitive) {
+ if (thisQualifiedName != null && isPatternMatch(String.valueOf(thisQualifiedName))) {
+ if (otherQualifiedName != null && otherQualifiedName.length > 0) {
+ if (!isCaseSensitive)
+ thisQualifiedName = CharOperation.toLowerCase(thisQualifiedName);
+ return CharOperation.match(thisQualifiedName, otherQualifiedName, isCaseSensitive);
+ }
+ }
+ return false;
+}
+
+/**
+ * Validate compatibility between given string pattern and match rule.
+ *<br>
+ * Optimized (ie. returned match rule is modified) combinations are:
+ * <ul>
+ * <li>{@link #R_PATTERN_MATCH} without any '*' or '?' in string pattern:
+ * pattern match bit is unset,
+ * </li>
+ * <li>{@link #R_PATTERN_MATCH} and {@link #R_PREFIX_MATCH} bits simultaneously set:
+ * prefix match bit is unset,
+ * </li>
+ * <li>{@link #R_PATTERN_MATCH} and {@link #R_CAMELCASE_MATCH} bits simultaneously set:
+ * camel case match bit is unset,
+ * </li>
+ * <li>{@link #R_CAMELCASE_MATCH} with invalid combination of uppercase and lowercase characters:
+ * camel case match bit is unset and replaced with prefix match pattern,
+ * </li>
+ * <li>{@link #R_CAMELCASE_MATCH} combined with {@link #R_PREFIX_MATCH} and {@link #R_CASE_SENSITIVE}
+ * bits is reduced to only {@link #R_CAMELCASE_MATCH} as Camel Case search is already prefix and case sensitive,
+ * </li>
+ * </ul>
+ *<br>
+ * Rejected (ie. returned match rule -1) combinations are:
+ * <ul>
+ * <li>{@link #R_REGEXP_MATCH} with any other match mode bit set,
+ * </li>
+ * </ul>
+ *
+ * @param stringPattern The string pattern
+ * @param matchRule The match rule
+ * @return Optimized valid match rule or -1 if an incompatibility was detected.
+ *
+ */
+public static int validateMatchRule(String stringPattern, int matchRule) {
+
+ // Verify Regexp match rule
+ if ((matchRule & R_REGEXP_MATCH) != 0) {
+ if ((matchRule & R_PATTERN_MATCH) != 0 || (matchRule & R_PREFIX_MATCH) != 0 || (matchRule & R_CAMELCASE_MATCH) != 0) {
+ return -1;
+ }
+ }
+
+ // Verify Pattern match rule
+ int starIndex = stringPattern.indexOf('*');
+ int questionIndex = stringPattern.indexOf('?');
+ if (starIndex < 0 && questionIndex < 0) {
+ // reset pattern match bit if any
+ matchRule &= ~R_PATTERN_MATCH;
+ } else {
+ // force Pattern rule
+ matchRule |= R_PATTERN_MATCH;
+ }
+ if ((matchRule & R_PATTERN_MATCH) != 0) {
+ // remove Camel Case and Prefix match bits if any
+ matchRule &= ~R_CAMELCASE_MATCH;
+ matchRule &= ~R_PREFIX_MATCH;
+ }
+
+ // Verify Camel Case match rule
+ if ((matchRule & R_CAMELCASE_MATCH) != 0) {
+ // Verify sting pattern validity
+ int length = stringPattern.length();
+ boolean validCamelCase = true;
+ boolean uppercase = false;
+ for (int i=0; i<length && validCamelCase; i++) {
+ char ch = stringPattern.charAt(i);
+ validCamelCase = ScannerHelper.isJavaIdentifierStart(ch);
+ // at least one uppercase character is need in CamelCase pattern
+ // (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=136313)
+ if (!uppercase) uppercase = ScannerHelper.isUpperCase(ch);
+ }
+ validCamelCase = validCamelCase && uppercase;
+ // Verify bits compatibility
+ if (validCamelCase) {
+ if ((matchRule & R_PREFIX_MATCH) != 0) {
+ if ((matchRule & R_CASE_SENSITIVE) != 0) {
+ // This is equivalent to Camel Case match rule
+ matchRule &= ~R_PREFIX_MATCH;
+ matchRule &= ~R_CASE_SENSITIVE;
+ }
+ }
+ } else {
+ matchRule &= ~R_CAMELCASE_MATCH;
+ if ((matchRule & R_PREFIX_MATCH) == 0) {
+ matchRule |= R_PREFIX_MATCH;
+ matchRule |= R_CASE_SENSITIVE;
+ }
+ }
+ }
+ return matchRule;
+}
+
+/**
+ * Returns whether the given pattern is a pattern case or not.
+ *
+ * @param pattern the pattern to inspect
+ * @return whether it is a pattern case or not
+ */
+public static boolean isPatternMatch(String pattern) {
+ return SearchPattern.validateMatchRule(
+ pattern,
+ SearchPattern.R_PATTERN_MATCH) == SearchPattern.R_PATTERN_MATCH;
+}
+
+/**
+ * @see java.lang.Object#toString()
+ */
+public String toString() {
+ return "SearchPattern"; //$NON-NLS-1$
+}
+}
|