/******************************************************************************* * Copyright (c) 2000, 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.core.search; import java.io.IOException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.IField; import org.eclipse.jdt.core.IImportDeclaration; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeParameter; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.InvalidInputException; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; import org.eclipse.jdt.internal.compiler.parser.Scanner; import org.eclipse.jdt.internal.compiler.parser.ScannerHelper; import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; import org.eclipse.jdt.internal.core.LocalVariable; import org.eclipse.jdt.internal.core.index.EntryResult; import org.eclipse.jdt.internal.core.index.Index; import org.eclipse.jdt.internal.core.search.HierarchyScope; import org.eclipse.jdt.internal.core.search.IndexQueryRequestor; import org.eclipse.jdt.internal.core.search.JavaSearchScope; import org.eclipse.jdt.internal.core.search.StringOperation; import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants; import org.eclipse.jdt.internal.core.search.matching.AndPattern; import org.eclipse.jdt.internal.core.search.matching.ConstructorPattern; import org.eclipse.jdt.internal.core.search.matching.FieldPattern; import org.eclipse.jdt.internal.core.search.matching.LocalVariablePattern; import org.eclipse.jdt.internal.core.search.matching.MatchLocator; import org.eclipse.jdt.internal.core.search.matching.MethodPattern; import org.eclipse.jdt.internal.core.search.matching.OrPattern; import org.eclipse.jdt.internal.core.search.matching.PackageDeclarationPattern; import org.eclipse.jdt.internal.core.search.matching.PackageReferencePattern; import org.eclipse.jdt.internal.core.search.matching.QualifiedTypeDeclarationPattern; import org.eclipse.jdt.internal.core.search.matching.SuperTypeReferencePattern; import org.eclipse.jdt.internal.core.search.matching.TypeDeclarationPattern; import org.eclipse.jdt.internal.core.search.matching.TypeParameterPattern; import org.eclipse.jdt.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 document is created for * this index entry using {@link SearchParticipant#getDocument(String)}. * * </p> * <p> * This class is intended to be sub-classed by clients. A default behavior is provided for each of * the methods above, that clients can override if they wish. * </p> * * @see #createPattern(org.eclipse.jdt.core.IJavaElement, int) * @see #createPattern(String, int, int, int) * @since 3.0 */ public abstract class SearchPattern { // 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 java 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. * * @since 3.1 */ 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 java 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}. * * @since 3.1 */ 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. * * @since 3.1 */ public static final int R_FULL_MATCH= 0x0040; /** * Match rule: The search pattern contains a Camel Case expression. * <p> * Examples: * <ul> * <li>'NPE' type string pattern will match 'NullPointerException' and 'NoPermissionException' * types,</li> * <li>'NuPoEx' type string pattern will only match 'NullPointerException' type.</li> * </ul> * * This rule is not intended to be combined with any other match rule. In case of other match * rule flags are combined with this one, then match rule validation will return a modified rule * in order to perform a better appropriate search request (see * {@link #validateMatchRule(String, int)} for more details). * <p> * * @see #camelCaseMatch(String, String) for a detailed explanation of Camel Case matching. * * @since 3.2 */ public static final int R_CAMELCASE_MATCH= 0x0080; /** * Match rule: The search pattern contains a Camel Case expression with a strict expected number * of parts. <br> * Examples: * <ul> * <li>'HM' type string pattern will match 'HashMap' and 'HtmlMapper' types, but not * 'HashMapEntry'</li> * <li>'HMap' type string pattern will still match previous 'HashMap' and 'HtmlMapper' types, * but not 'HighMagnitude'</li> * </ul> * * This rule is not intended to be combined with any other match rule. In case of other match * rule flags are combined with this one, then match rule validation will return a modified rule * in order to perform a better appropriate search request (see * {@link #validateMatchRule(String, int)} for more details). * <p> * * @see CharOperation#camelCaseMatch(char[], char[], boolean) for a detailed explanation of * Camel Case matching. * <p> * @since 3.4 */ public static final int R_CAMELCASE_SAME_PART_COUNT_MATCH= 0x0100; private static final int MODE_MASK= R_EXACT_MATCH | R_PREFIX_MATCH | R_PATTERN_MATCH | R_REGEXP_MATCH | R_CAMELCASE_MATCH | R_CAMELCASE_SAME_PART_COUNT_MATCH; private int matchRule; /** * The focus element (used for reference patterns) * * @noreference This field is not intended to be referenced by clients. */ public IJavaElement focus; /** * @noreference This field is not intended to be referenced by clients. */ public int kind; /** * @noreference This field is not intended to be referenced by clients. */ public boolean mustResolve= true; /** * 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 following match rule * <ul> * <li>{@link #R_EXACT_MATCH}</li> * <li>{@link #R_PREFIX_MATCH}</li> * <li>{@link #R_PATTERN_MATCH}</li> * <li>{@link #R_REGEXP_MATCH}</li> * <li>{@link #R_CAMELCASE_MATCH}</li> * <li>{@link #R_CAMELCASE_SAME_PART_COUNT_MATCH}</li> * </ul> * which may be also combined with one of following flag: * <ul> * <li>{@link #R_CASE_SENSITIVE}</li> * <li>{@link #R_ERASURE_MATCH}</li> * <li>{@link #R_EQUIVALENT_MATCH}</li> * </ul> * For example, * <ul> * <li>{@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE}: if an exact and case * sensitive match is requested,</li> * <li>{@link #R_PREFIX_MATCH} if a case insensitive prefix match is requested</li> * <li>{@link #R_EXACT_MATCH} | {@link #R_ERASURE_MATCH}: if a case insensitive and * erasure match is requested.</li> * </ul> * Note that {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH} has no effect on * non-generic types/methods search. * <p> * 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; } // reset other incompatible flags if ((matchRule & R_CAMELCASE_MATCH) != 0) { this.matchRule&= ~R_CAMELCASE_SAME_PART_COUNT_MATCH; this.matchRule&= ~R_PREFIX_MATCH; } else if ((matchRule & R_CAMELCASE_SAME_PART_COUNT_MATCH) != 0) { this.matchRule&= ~R_PREFIX_MATCH; } } /** * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. */ public void acceptMatch(String relativePath, String containerPath, char separator, SearchPattern pattern, IndexQueryRequestor requestor, SearchParticipant participant, IJavaSearchScope scope) { acceptMatch(relativePath, containerPath, separator, pattern, requestor, participant, scope, null); } /** * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. */ public void acceptMatch(String relativePath, String containerPath, char separator, SearchPattern pattern, IndexQueryRequestor requestor, SearchParticipant participant, IJavaSearchScope scope, IProgressMonitor monitor) { if (scope instanceof JavaSearchScope) { JavaSearchScope javaSearchScope= (JavaSearchScope)scope; // Get document path access restriction from java search scope // Note that requestor has to verify if needed whether the document violates the access restriction or not AccessRuleSet access= javaSearchScope.getAccessRuleSet(relativePath, containerPath); if (access != JavaSearchScope.NOT_ENCLOSED) { // scope encloses the document path StringBuffer documentPath= new StringBuffer(containerPath.length() + 1 + relativePath.length()); documentPath.append(containerPath); documentPath.append(separator); documentPath.append(relativePath); if (!requestor.acceptIndexMatch(documentPath.toString(), pattern, participant, access)) throw new OperationCanceledException(); } } else { StringBuffer buffer= new StringBuffer(containerPath.length() + 1 + relativePath.length()); buffer.append(containerPath); buffer.append(separator); buffer.append(relativePath); String documentPath= buffer.toString(); boolean encloses= (scope instanceof HierarchyScope) ? ((HierarchyScope)scope).encloses(documentPath, monitor) : scope.encloses(documentPath); if (encloses) if (!requestor.acceptIndexMatch(documentPath, pattern, participant, null)) throw new OperationCanceledException(); } } /** * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. */ public SearchPattern currentPattern() { return this; } /** * Answers true if the pattern matches the given name using CamelCase rules, or false otherwise. * char[] CamelCase matching does NOT accept explicit wild-cards '*' and '?' and is inherently * case sensitive. * <p> * 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. * <p> * The pattern may contain lowercase characters, which will be matched 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'. * <p> * Digit characters are treated in a special way. They can be used in the pattern but are not * always considered as leading character. For instance, both 'UTF16DSS' and 'UTFDSS' patterns * will match 'UTF16DocumentScannerSupport'. * <p> * Using this method allows matching names to have more parts than the specified pattern (see * {@link #camelCaseMatch(String, String, boolean)}).<br> * For instance, 'HM' , 'HaMa' and 'HMap' patterns will match 'HashMap', 'HatMapper' <b>and * also</b> 'HashMapEntry'. * <p> * * <pre> * Examples: * <ol><li> pattern = "NPE" * name = NullPointerException / NoPermissionException * result => true</li> * <li> pattern = "NuPoEx" * name = NullPointerException * result => true</li> * <li> pattern = "npe" * name = NullPointerException * result => false</li> * <li> pattern = "IPL3" * name = "IPerspectiveListener3" * result => true</li> * <li> pattern = "HM" * name = "HashMapEntry" * result => true</li> * <li> pattern = "HMap" * name = "HatMapper" * result => true</li> * </ol> * </pre> * * @see #camelCaseMatch(String, int, int, String, int, int, boolean) for algorithm * implementation * * @param pattern the given pattern * @param name the given name * @return true if the pattern matches the given name, false otherwise * @since 3.2 */ 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(), false/*not the same count of parts*/); } /** * Answers true if the pattern matches the given name using CamelCase rules, or false otherwise. * char[] CamelCase matching does NOT accept explicit wild-cards '*' and '?' and is inherently * case sensitive. * <p> * 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. * <p> * The pattern may contain lowercase characters, which will be matched 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'. * <p> * Digit characters are treated in a special way. They can be used in the pattern but are not * always considered as leading character. For instance, both 'UTF16DSS' and 'UTFDSS' patterns * will match 'UTF16DocumentScannerSupport'. * <p> * CamelCase can be restricted to match only the same count of parts. When this restriction is * specified the given pattern and the given name must have <b>exactly</b> the same number of * parts (i.e. the same number of uppercase characters).<br> * For instance, 'HM' , 'HaMa' and 'HMap' patterns will match 'HashMap' and 'HatMapper' <b>but * not</b> 'HashMapEntry'. * <p> * * <pre> * Examples: * <ol><li> pattern = "NPE" * name = NullPointerException / NoPermissionException * result => true</li> * <li> pattern = "NuPoEx" * name = NullPointerException * result => true</li> * <li> pattern = "npe" * name = NullPointerException * result => false</li> * <li> pattern = "IPL3" * name = "IPerspectiveListener3" * result => true</li> * <li> pattern = "HM" * name = "HashMapEntry" * result => (samePartCount == false)</li> * </ol> * </pre> * * @see #camelCaseMatch(String, int, int, String, int, int, boolean) for algorithm * implementation * * @param pattern the given pattern * @param name the given name * @param samePartCount flag telling whether the pattern and the name should have the same count * of parts or not.<br> *   For example: * <ul> * <li>'HM' type string pattern will match 'HashMap' and 'HtmlMapper' types, but not * 'HashMapEntry'</li> <li>'HMap' type string pattern will still match previous * 'HashMap' and 'HtmlMapper' types, but not 'HighMagnitude'</li> * </ul> * @return true if the pattern matches the given name, false otherwise * @since 3.4 */ public static final boolean camelCaseMatch(String pattern, String name, boolean samePartCount) { 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(), samePartCount); } /** * Answers true if a sub-pattern matches the sub-part of the given name using CamelCase rules, * or false otherwise. char[] 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 sub-pattern is defined by the patternStart and patternEnd * positions. * <p> * 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. * <p> * The pattern may contain lowercase characters, which will be matched 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'. * <p> * Digit characters are treated in a special way. They can be used in the pattern but are not * always considered as leading character. For instance, both 'UTF16DSS' and 'UTFDSS' patterns * will match 'UTF16DocumentScannerSupport'. * <p> * Digit characters are treated in a special way. They can be used in the pattern but are not * always considered as leading character. For instance, both 'UTF16DSS' and 'UTFDSS' patterns * will match 'UTF16DocumentScannerSupport'. * <p> * Using this method allows matching names to have more parts than the specified pattern (see * {@link #camelCaseMatch(String, int, int, String, int, int, boolean)}).<br> * For instance, 'HM' , 'HaMa' and 'HMap' patterns will match 'HashMap', 'HatMapper' <b>and * also</b> 'HashMapEntry'. * <p> * * <pre> * Examples:<ol> * <li> pattern = "NPE" * patternStart = 0 * patternEnd = 3 * name = NullPointerException * nameStart = 0 * nameEnd = 20 * result => true</li> * <li> pattern = "NPE" * patternStart = 0 * patternEnd = 3 * name = NoPermissionException * nameStart = 0 * nameEnd = 21 * result => true</li> * <li> pattern = "NuPoEx" * patternStart = 0 * patternEnd = 6 * name = NullPointerException * nameStart = 0 * nameEnd = 20 * result => true</li> * <li> pattern = "NuPoEx" * patternStart = 0 * patternEnd = 6 * name = NoPermissionException * nameStart = 0 * nameEnd = 21 * result => false</li> * <li> pattern = "npe" * patternStart = 0 * patternEnd = 3 * name = NullPointerException * nameStart = 0 * nameEnd = 20 * result => false</li> * <li> pattern = "IPL3" * patternStart = 0 * patternEnd = 3 * name = "IPerspectiveListener3" * nameStart = 0 * nameEnd = 21 * result => true</li> * <li> pattern = "HM" * patternStart = 0 * patternEnd = 2 * name = "HashMapEntry" * nameStart = 0 * nameEnd = 12 * result => true</li> * <li> pattern = "HMap" * patternStart = 0 * patternEnd = 4 * name = "HatMapper" * nameStart = 0 * nameEnd = 9 * result => true</li> * </ol> * </pre> * * @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 sub-part of the given name, false otherwise * @since 3.2 */ public static final boolean camelCaseMatch(String pattern, int patternStart, int patternEnd, String name, int nameStart, int nameEnd) { return camelCaseMatch(pattern, patternStart, patternEnd, name, nameStart, nameEnd, false/*not the same count of parts*/); } /** * Answers true if a sub-pattern matches the sub-part of the given name using CamelCase rules, * or false otherwise. char[] 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 sub-pattern is defined by the patternStart and patternEnd * positions. * <p> * 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. * <p> * The pattern may contain lowercase characters, which will be matched 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'. * <p> * Digit characters are treated in a special way. They can be used in the pattern but are not * always considered as leading character. For instance, both 'UTF16DSS' and 'UTFDSS' patterns * will match 'UTF16DocumentScannerSupport'. * <p> * CamelCase can be restricted to match only the same count of parts. When this restriction is * specified the given pattern and the given name must have <b>exactly</b> the same number of * parts (i.e. the same number of uppercase characters).<br> * For instance, 'HM' , 'HaMa' and 'HMap' patterns will match 'HashMap' and 'HatMapper' <b>but * not</b> 'HashMapEntry'. * <p> * * <pre> * Examples:<ol> * <li> pattern = "NPE" * patternStart = 0 * patternEnd = 3 * name = NullPointerException * nameStart = 0 * nameEnd = 20 * result => true</li> * <li> pattern = "NPE" * patternStart = 0 * patternEnd = 3 * name = NoPermissionException * nameStart = 0 * nameEnd = 21 * result => true</li> * <li> pattern = "NuPoEx" * patternStart = 0 * patternEnd = 6 * name = NullPointerException * nameStart = 0 * nameEnd = 20 * result => true</li> * <li> pattern = "NuPoEx" * patternStart = 0 * patternEnd = 6 * name = NoPermissionException * nameStart = 0 * nameEnd = 21 * result => false</li> * <li> pattern = "npe" * patternStart = 0 * patternEnd = 3 * name = NullPointerException * nameStart = 0 * nameEnd = 20 * result => false</li> * <li> pattern = "IPL3" * patternStart = 0 * patternEnd = 3 * name = "IPerspectiveListener3" * nameStart = 0 * nameEnd = 21 * result => true</li> * <li> pattern = "HM" * patternStart = 0 * patternEnd = 2 * name = "HashMapEntry" * nameStart = 0 * nameEnd = 12 * result => (samePartCount == false)</li> * </ol> * </pre> * * @see CharOperation#camelCaseMatch(char[], int, int, char[], int, int, boolean) from which * algorithm implementation has been entirely copied. * * @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 * @param samePartCount flag telling whether the pattern and the name should have the same count * of parts or not.<br> *   For example: * <ul> * <li>'HM' type string pattern will match 'HashMap' and 'HtmlMapper' types, but not * 'HashMapEntry'</li> <li>'HMap' type string pattern will still match previous * 'HashMap' and 'HtmlMapper' types, but not 'HighMagnitude'</li> * </ul> * @return true if a sub-pattern matches the sub-part of the given name, false otherwise * @since 3.4 */ public static final boolean camelCaseMatch(String pattern, int patternStart, int patternEnd, String name, int nameStart, int nameEnd, boolean samePartCount) { return StringOperation.getCamelCaseMatchingRegions(pattern, patternStart, patternEnd, name, nameStart, nameEnd, samePartCount) != null; } /** * Answers all the regions in a given name matching a given pattern using a specified match * rule. </p> * <p> * Each of these regions is made of its starting index and its length in the given name. They * are all concatenated in a single array of <code>int</code> which therefore always has an even * length. * </p> * <p> * All returned regions are disjointed from each other. That means that the end of a region is * always different than the start of the following one.<br> * For example, if two regions are returned:<br> * <code>{ start1, length1, start2, length2 }</code><br> * then <code>start1+length1</code> will always be smaller than <code>start2</code>. * </p> * <p> * The possible comparison rules between the name and the pattern are: * <ul> * <li>{@link #R_EXACT_MATCH exact matching}</li> * <li>{@link #R_PREFIX_MATCH prefix matching}</li> * <li>{@link #R_PATTERN_MATCH pattern matching}</li> * <li>{@link #R_CAMELCASE_MATCH camel case matching}</li> * <li>{@link #R_CAMELCASE_SAME_PART_COUNT_MATCH camel case matching with same parts count}</li> * </ul> * Each of these rules may be combined with the {@link #R_CASE_SENSITIVE case sensitive flag} if * the match comparison should respect the case. * * <pre> * Examples: * <ol><li> pattern = "NPE" * name = NullPointerException / NoPermissionException * matchRule = {@link #R_CAMELCASE_MATCH} * result: { 0, 1, 4, 1, 11, 1 } / { 0, 1, 2, 1, 12, 1 } </li> * <li> pattern = "NuPoEx" * name = NullPointerException * matchRule = {@link #R_CAMELCASE_MATCH} * result: { 0, 2, 4, 2, 11, 2 }</li> * <li> pattern = "IPL3" * name = "IPerspectiveListener3" * matchRule = {@link #R_CAMELCASE_MATCH} * result: { 0, 2, 12, 1, 20, 1 }</li> * <li> pattern = "HashME" * name = "HashMapEntry" * matchRule = {@link #R_CAMELCASE_MATCH} * result: { 0, 5, 7, 1 }</li> * <li> pattern = "N???Po*Ex?eption" * name = NullPointerException * matchRule = {@link #R_PATTERN_MATCH} | {@link #R_CASE_SENSITIVE} * result: { 0, 1, 4, 2, 11, 2, 14, 6 }</li> * <li> pattern = "Ha*M*ent*" * name = "HashMapEntry" * matchRule = {@link #R_PATTERN_MATCH} * result: { 0, 2, 4, 1, 7, 3 }</li> * </ol> * </pre> * * @see #camelCaseMatch(String, String, boolean) for more details on the camel case behavior * @see CharOperation#match(char[], char[], boolean) for more details on the pattern match * behavior * * @param pattern the given pattern. If <code>null</code>, then an empty region ( * <code>new int[0]</code>) will be returned showing that the name matches the * pattern but no common character has been found. * @param name the given name * @param matchRule the rule to apply for the comparison.<br> * The following values are accepted: * <ul> * <li>{@link #R_EXACT_MATCH}</li> <li>{@link #R_PREFIX_MATCH}</li> <li> * {@link #R_PATTERN_MATCH}</li> <li>{@link #R_CAMELCASE_MATCH}</li> <li> * {@link #R_CAMELCASE_SAME_PART_COUNT_MATCH}</li> * </ul> * <p> * Each of these valid values may be also combined with the {@link #R_CASE_SENSITIVE} * flag. * </p> * Some examples: * <ul> * <li>{@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE}: if an exact case sensitive * match is expected,</li> * <li>{@link #R_PREFIX_MATCH}: if a case insensitive prefix match is expected,</li> * <li>{@link #R_CAMELCASE_MATCH}: if a case insensitive camel case match is * expected,</li> * <li>{@link #R_CAMELCASE_SAME_PART_COUNT_MATCH} | {@link #R_CASE_SENSITIVE}: if a * case sensitive camel case with same parts count match is expected,</li> * <li>etc.</li> * </ul> * @return an array of <code>int</code> having two slots per returned regions (the first one is * the region starting index and the second one is the region length or * <code>null</code> if the given name does not match the given pattern). * <p> * The returned regions may be empty (<code>new int[0]</code>) if the pattern is * <code>null</code> (whatever the match rule is). The returned regions will also be * empty if the pattern is only made of <code>'?'</code> and/or <code>'*'</code> * character(s) (e.g. <code>'*'</code>, <code>'?*'</code>, <code>'???'</code>, etc.) * when using a pattern match rule. * </p> * * @since 3.5 */ public static final int[] getMatchingRegions(String pattern, String name, int matchRule) { if (name == null) return null; final int nameLength= name.length(); if (pattern == null) { return new int[] { 0, nameLength }; } final int patternLength= pattern.length(); boolean countMatch= false; switch (matchRule) { case SearchPattern.R_EXACT_MATCH: if (patternLength == nameLength && pattern.equalsIgnoreCase(name)) { return new int[] { 0, patternLength }; } break; case SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE: if (patternLength == nameLength && pattern.equals(name)) { return new int[] { 0, patternLength }; } break; case SearchPattern.R_PREFIX_MATCH: if (patternLength <= nameLength && name.substring(0, patternLength).equalsIgnoreCase(pattern)) { return new int[] { 0, patternLength }; } break; case SearchPattern.R_PREFIX_MATCH | SearchPattern.R_CASE_SENSITIVE: if (name.startsWith(pattern)) { return new int[] { 0, patternLength }; } break; case SearchPattern.R_CAMELCASE_SAME_PART_COUNT_MATCH: countMatch= true; //$FALL-THROUGH$ case SearchPattern.R_CAMELCASE_MATCH: if (patternLength <= nameLength) { int[] regions= StringOperation.getCamelCaseMatchingRegions(pattern, 0, patternLength, name, 0, nameLength, countMatch); if (regions != null) return regions; if (name.substring(0, patternLength).equalsIgnoreCase(pattern)) { return new int[] { 0, patternLength }; } } break; case SearchPattern.R_CAMELCASE_SAME_PART_COUNT_MATCH | SearchPattern.R_CASE_SENSITIVE: countMatch= true; //$FALL-THROUGH$ case SearchPattern.R_CAMELCASE_MATCH | SearchPattern.R_CASE_SENSITIVE: if (patternLength <= nameLength) { return StringOperation.getCamelCaseMatchingRegions(pattern, 0, patternLength, name, 0, nameLength, countMatch); } break; case SearchPattern.R_PATTERN_MATCH: return StringOperation.getPatternMatchingRegions(pattern, 0, patternLength, name, 0, nameLength, false); case SearchPattern.R_PATTERN_MATCH | SearchPattern.R_CASE_SENSITIVE: return StringOperation.getPatternMatchingRegions(pattern, 0, patternLength, name, 0, nameLength, true); } return null; } /** * 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 * @deprecated Unfortunately, this functionality is not fully supported yet (see * "https://bugs.eclipse.org/bugs/show_bug.cgi?id=142044" for more details). This * might be done in a further version... */ public static SearchPattern createAndPattern(SearchPattern leftPattern, SearchPattern rightPattern) { return new AndPattern(leftPattern, rightPattern); } private static SearchPattern createFieldPattern(String patternString, int limitTo, int matchRule) { 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 Java 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 Java 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 return new FieldPattern( fieldNameChars, declaringTypeQualification, declaringTypeSimpleName, typeQualification, typeSimpleName, limitTo, matchRule); } private static SearchPattern createMethodOrConstructorPattern(String patternString, int limitTo, int matchRule, boolean isConstructor) { 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) { typeArgumentsString= scanner.getCurrentTokenString(); mode= InsideTypeArguments; break; } if (declaringType == null) { declaringType= selector; } else { declaringType+= '.' + selector; } declaringType+= scanner.getCurrentTokenString(); selector= null; break; case TerminalTokens.TokenNameDOT: if (!isConstructor && 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 Java 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) { String pseudoType= "Type" + typeArgumentsString; //$NON-NLS-1$ typeArguments= Signature.getTypeArguments(Signature.createTypeSignature(pseudoType, false).toCharArray()); 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$ - fall through next case to add token default: // all other tokens are considered identifiers (see bug 21763 Problem in Java 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$ - fall through next case to add token default: // all other tokens are considered identifiers (see bug 21763 Problem in Java 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); if (declaringTypeSignature.indexOf(Signature.C_GENERIC_START) < 0) { declaringTypePart= declaringType.toCharArray(); } else { declaringTypePart= Signature.toCharArray(Signature.getTypeErasure(declaringTypeSignature.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); if (parameterTypeSignatures[i].indexOf(Signature.C_GENERIC_START) < 0) { parameterTypePart= parameterTypes[i].toCharArray(); } else { parameterTypePart= Signature.toCharArray(Signature.getTypeErasure(parameterTypeSignatures[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); if (returnTypeSignature.indexOf(Signature.C_GENERIC_START) < 0) { returnTypePart= returnType.toCharArray(); } else { returnTypePart= Signature.toCharArray(Signature.getTypeErasure(returnTypeSignature.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 if (isConstructor) { return new ConstructorPattern( declaringTypeSimpleName, declaringTypeQualification, declaringTypeSignature, parameterTypeQualifications, parameterTypeSimpleNames, parameterTypeSignatures, typeArguments, limitTo, matchRule); } else { return new MethodPattern( selectorChars, declaringTypeQualification, declaringTypeSimpleName, declaringTypeSignature, returnTypeQualification, returnTypeSimpleName, returnTypeSignature, parameterTypeQualifications, parameterTypeSimpleNames, parameterTypeSignatures, typeArguments, limitTo, 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 IJavaSearchConstants.DECLARATIONS: return new PackageDeclarationPattern(patternString.toCharArray(), matchRule); case IJavaSearchConstants.REFERENCES: return new PackageReferencePattern(patternString.toCharArray(), matchRule); case IJavaSearchConstants.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", IJavaSearchConstants.TYPE, IJavaSearchConstants.REFERENCES, false);</code> * </li> * <li>search for case sensitive references to exact <code>Object()</code> constructor: * <code>createSearchPattern("java.lang.Object()", IJavaSearchConstants.CONSTRUCTOR, IJavaSearchConstants.REFERENCES, true);</code> * </li> * <li>search for implementers of <code>java.lang.Runnable</code>: * <code>createSearchPattern("java.lang.Runnable", IJavaSearchConstants.TYPE, IJavaSearchConstants.IMPLEMENTORS, true);</code> * </li> * </ul> * * @param stringPattern the given pattern * <ul> * <li>Type patterns have the following syntax: * <p> * <b><code>[qualification '.']typeName ['<' typeArguments '>']</code></b> * </p> * <p> * Some samples:<code> * <ul> * <li>java.lang.Object</li> * <li>Runnable</li> * <li>List<String></li> * </ul> * </code> </p> * <p> * Type arguments can be specified to search for references to parameterized types * using following syntax: * </p> * <p> * <b> * <code>'<' { [ '?' {'extends'|'super'} ] type ( ',' [ '?' {'extends'|'super'} ] type )* | '?' } '>'</code> * </b> * </p> * <p> * <i> Note that: * <ul> * <li>'*' is not valid inside type arguments definition <></li> * <li>'?' is treated as a wildcard when it is inside <> (i.e. it must be put * on first position of the type argument)</li> * </ul> * </i> * </p> * </li> <li>Method patterns have the following syntax: * <p> * <b> * <code>[declaringType '.'] ['<' typeArguments '>'] methodName ['(' parameterTypes ')'] [returnType]</code> * </b> * </p> * <p> * Type arguments have the same syntax as explained in the type patterns section * </p> * <p> * Some samples:<code> * <ul> * <li>java.lang.Runnable.run() void</li> <li>main(*)</li> <li> * <String>toArray(String[])</li> </ul> </code> </p> </li> <li>Constructor * patterns have the following syntax: * <p> * <b> * <code>['<' typeArguments '>'] [declaringQualification '.'] typeName ['(' parameterTypes ')']</code> * </b> * </p> * <p> * Type arguments have the same syntax as explained in the type patterns section * </p> * <p> * Some samples:<code> * <ul> * <li>java.lang.Object()</li> <li>Test(*)</li> <li><Exception>Sample(Exception)</li> * </ul> </code> </p> </li> <li>Field patterns have the following syntax: * <p> * <b><code>[declaringType '.'] fieldName [fieldType]</code></b> * </p> * <p> * Some samples:<code> * <ul> * <li>java.lang.String.serialVersionUID long</li> <li>field*</li> </ul> </code> </p> </li> * <li>Package patterns have the following syntax: * <p> * <b><code>packageNameSegment {'.' packageNameSegment}</code></b> * </p> * <p> * Some samples:<code> * <ul> * <li>java.lang</li> <li>org.e*.jdt.c*e</li> </ul> </code> </p> </li> </ul> * @param searchFor determines the nature of the searched elements * <ul> * <li>{@link IJavaSearchConstants#CLASS}: only look for classes</li> * <li>{@link IJavaSearchConstants#INTERFACE}: only look for interfaces</li> * <li>{@link IJavaSearchConstants#ENUM}: only look for enumeration</li> * <li>{@link IJavaSearchConstants#ANNOTATION_TYPE}: only look for annotation type</li> * <li>{@link IJavaSearchConstants#CLASS_AND_ENUM}: only look for classes and * enumerations</li> * <li>{@link IJavaSearchConstants#CLASS_AND_INTERFACE}: only look for classes and * interfaces</li> * <li>{@link IJavaSearchConstants#TYPE}: look for all types (i.e. classes, * interfaces, enum and annotation types)</li> * <li>{@link IJavaSearchConstants#FIELD}: look for fields</li> * <li>{@link IJavaSearchConstants#METHOD}: look for methods</li> * <li>{@link IJavaSearchConstants#CONSTRUCTOR}: look for constructors</li> * <li>{@link IJavaSearchConstants#PACKAGE}: look for packages</li> * </ul> * @param limitTo determines the nature of the expected matches * <ul> * <li>{@link IJavaSearchConstants#DECLARATIONS DECLARATIONS}: will search * declarations matching with the corresponding element. In case the element is a * method, declarations of matching methods in sub-types will also be found, allowing * to find declarations of abstract methods, etc.<br> * Note that additional flags {@link IJavaSearchConstants#IGNORE_DECLARING_TYPE * IGNORE_DECLARING_TYPE} and {@link IJavaSearchConstants#IGNORE_RETURN_TYPE * 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 IJavaSearchConstants#REFERENCES REFERENCES}: will search references to * the given element.</li> * <li>{@link IJavaSearchConstants#ALL_OCCURRENCES ALL_OCCURRENCES}: will search for * either declarations or references as specified above.</li> * <li>{@link IJavaSearchConstants#IMPLEMENTORS 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 IJavaSearchConstants#CLASS CLASS} or * {@link IJavaSearchConstants#INTERFACE INTERFACE} is respectively used instead of * {@link IJavaSearchConstants#TYPE TYPE}.</li> * <li>All other fine grain constants defined in the <b>limitTo</b> category of the * {@link IJavaSearchConstants} are also accepted nature: * <table border=0> * <tr> * <th align=left>Fine grain constant * <th align=left>Meaning * <tr> * <td>{@link IJavaSearchConstants#FIELD_DECLARATION_TYPE_REFERENCE * FIELD_DECLARATION_TYPE_REFERENCE} * <td>Return only type references used as the type of a field declaration. * <tr> * <td>{@link IJavaSearchConstants#LOCAL_VARIABLE_DECLARATION_TYPE_REFERENCE * LOCAL_VARIABLE_DECLARATION_TYPE_REFERENCE} * <td>Return only type references used as the type of a local variable declaration. * <tr> * <td>{@link IJavaSearchConstants#PARAMETER_DECLARATION_TYPE_REFERENCE * PARAMETER_DECLARATION_TYPE_REFERENCE} * <td>Return only type references used as the type of a method parameter * declaration. * <tr> * <td>{@link IJavaSearchConstants#SUPERTYPE_TYPE_REFERENCE SUPERTYPE_TYPE_REFERENCE} * <td>Return only type references used as a super type or as a super interface. * <tr> * <td>{@link IJavaSearchConstants#THROWS_CLAUSE_TYPE_REFERENCE * THROWS_CLAUSE_TYPE_REFERENCE} * <td>Return only type references used in a throws clause. * <tr> * <td>{@link IJavaSearchConstants#CAST_TYPE_REFERENCE CAST_TYPE_REFERENCE} * <td>Return only type references used in a cast expression. * <tr> * <td>{@link IJavaSearchConstants#CATCH_TYPE_REFERENCE CATCH_TYPE_REFERENCE} * <td>Return only type references used in a catch header. * <tr> * <td>{@link IJavaSearchConstants#CLASS_INSTANCE_CREATION_TYPE_REFERENCE * CLASS_INSTANCE_CREATION_TYPE_REFERENCE} * <td>Return only type references used in class instance creation. * <tr> * <td>{@link IJavaSearchConstants#RETURN_TYPE_REFERENCE RETURN_TYPE_REFERENCE} * <td>Return only type references used as a method return type. * <tr> * <td>{@link IJavaSearchConstants#IMPORT_DECLARATION_TYPE_REFERENCE * IMPORT_DECLARATION_TYPE_REFERENCE} * <td>Return only type references used in an import declaration. * <tr> * <td>{@link IJavaSearchConstants#ANNOTATION_TYPE_REFERENCE * ANNOTATION_TYPE_REFERENCE} * <td>Return only type references used as an annotation. * <tr> * <td>{@link IJavaSearchConstants#TYPE_ARGUMENT_TYPE_REFERENCE * TYPE_ARGUMENT_TYPE_REFERENCE} * <td>Return only type references used as a type argument in a parameterized type or * a parameterized method. * <tr> * <td>{@link IJavaSearchConstants#TYPE_VARIABLE_BOUND_TYPE_REFERENCE * TYPE_VARIABLE_BOUND_TYPE_REFERENCE} * <td>Return only type references used as a type variable bound. * <tr> * <td>{@link IJavaSearchConstants#WILDCARD_BOUND_TYPE_REFERENCE * WILDCARD_BOUND_TYPE_REFERENCE} * <td>Return only type references used as a wildcard bound. * <tr> * <td>{@link IJavaSearchConstants#INSTANCEOF_TYPE_REFERENCE * INSTANCEOF_TYPE_REFERENCE} * <td>Return only type references used as a type of an <code>instanceof</code> * expression. * <tr> * <td>{@link IJavaSearchConstants#SUPER_REFERENCE SUPER_REFERENCE} * <td>Return only super field accesses or super method invocations (e.g. using the * <code>super</code> qualifier). * <tr> * <td>{@link IJavaSearchConstants#QUALIFIED_REFERENCE QUALIFIED_REFERENCE} * <td>Return only qualified field accesses or qualified method invocations. * <tr> * <td>{@link IJavaSearchConstants#THIS_REFERENCE THIS_REFERENCE} * <td>Return only primary field accesses or primary method invocations (e.g. using * the <code>this</code> qualifier). * <tr> * <td>{@link IJavaSearchConstants#IMPLICIT_THIS_REFERENCE IMPLICIT_THIS_REFERENCE} * <td>Return only field accesses or method invocations without any qualification. * </table> * </li> * </ul> * @param matchRule one of the following match rules * <ul> * <li>{@link #R_EXACT_MATCH}</li> * <li>{@link #R_PREFIX_MATCH}</li> * <li>{@link #R_PATTERN_MATCH}</li> * <li>{@link #R_REGEXP_MATCH}</li> * <li>{@link #R_CAMELCASE_MATCH}</li> * <li>{@link #R_CAMELCASE_SAME_PART_COUNT_MATCH}</li> * </ul> * , which may be also combined with one of the following flags: * <ul> * <li>{@link #R_CASE_SENSITIVE}</li> * <li>{@link #R_ERASURE_MATCH}</li> * <li>{@link #R_EQUIVALENT_MATCH}</li> * </ul> * For example, * <ul> * <li>{@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE}: if an exact and case * sensitive match is requested,</li> * <li>{@link #R_PREFIX_MATCH} if a case insensitive prefix match is requested</li> * <li>{@link #R_EXACT_MATCH} | {@link #R_ERASURE_MATCH}: if a case insensitive and * erasure match is requested.</li> * </ul> * Note that {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH} has no effect on * non-generic types/methods search. * <p> * Note also that the 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&= ~(IJavaSearchConstants.IGNORE_DECLARING_TYPE + IJavaSearchConstants.IGNORE_RETURN_TYPE); switch (searchFor) { case IJavaSearchConstants.CLASS: return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.CLASS_SUFFIX); case IJavaSearchConstants.CLASS_AND_INTERFACE: return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.CLASS_AND_INTERFACE_SUFFIX); case IJavaSearchConstants.CLASS_AND_ENUM: return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.CLASS_AND_ENUM_SUFFIX); case IJavaSearchConstants.INTERFACE: return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.INTERFACE_SUFFIX); case IJavaSearchConstants.INTERFACE_AND_ANNOTATION: return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.INTERFACE_AND_ANNOTATION_SUFFIX); case IJavaSearchConstants.ENUM: return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.ENUM_SUFFIX); case IJavaSearchConstants.ANNOTATION_TYPE: return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.ANNOTATION_TYPE_SUFFIX); case IJavaSearchConstants.TYPE: return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.TYPE_SUFFIX); case IJavaSearchConstants.METHOD: return createMethodOrConstructorPattern(stringPattern, limitTo, matchRule, false/*not a constructor*/); case IJavaSearchConstants.CONSTRUCTOR: return createMethodOrConstructorPattern(stringPattern, limitTo, matchRule, true/*constructor*/); case IJavaSearchConstants.FIELD: return createFieldPattern(stringPattern, limitTo, matchRule); case IJavaSearchConstants.PACKAGE: return createPackagePattern(stringPattern, limitTo, matchRule); } return null; } /** * Returns a search pattern based on a given Java 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 (i.e. {@link #R_EXACT_MATCH} or * {@link #R_EQUIVALENT_MATCH}) are expected, {@link #createPattern(IJavaElement, 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 Java element the search pattern is based on * @param limitTo determines the nature of the expected matches * <ul> * <li>{@link IJavaSearchConstants#DECLARATIONS DECLARATIONS}: will search * declarations matching with the corresponding element. In case the element is a * method, declarations of matching methods in sub-types will also be found, allowing * to find declarations of abstract methods, etc. Some additional flags may be * specified while searching declaration: * <ul> * <li>{@link IJavaSearchConstants#IGNORE_DECLARING_TYPE 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 IJavaSearchConstants#IGNORE_RETURN_TYPE 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 IJavaSearchConstants#REFERENCES REFERENCES}: will search references to * the given element.</li> * <li>{@link IJavaSearchConstants#ALL_OCCURRENCES ALL_OCCURRENCES}: will search for * either declarations or references as specified above.</li> * <li>All other fine grain constants defined in the <b>limitTo</b> category of the * {@link IJavaSearchConstants} are also accepted nature: * <table border=0> * <tr> * <th align=left>Fine grain constant * <th align=left>Meaning * <tr> * <td>{@link IJavaSearchConstants#FIELD_DECLARATION_TYPE_REFERENCE * FIELD_DECLARATION_TYPE_REFERENCE} * <td>Return only type references used as the type of a field declaration. * <tr> * <td>{@link IJavaSearchConstants#LOCAL_VARIABLE_DECLARATION_TYPE_REFERENCE * LOCAL_VARIABLE_DECLARATION_TYPE_REFERENCE} * <td>Return only type references used as the type of a local variable declaration. * <tr> * <td>{@link IJavaSearchConstants#PARAMETER_DECLARATION_TYPE_REFERENCE * PARAMETER_DECLARATION_TYPE_REFERENCE} * <td>Return only type references used as the type of a method parameter * declaration. * <tr> * <td>{@link IJavaSearchConstants#SUPERTYPE_TYPE_REFERENCE SUPERTYPE_TYPE_REFERENCE} * <td>Return only type references used as a super type or as a super interface. * <tr> * <td>{@link IJavaSearchConstants#THROWS_CLAUSE_TYPE_REFERENCE * THROWS_CLAUSE_TYPE_REFERENCE} * <td>Return only type references used in a throws clause. * <tr> * <td>{@link IJavaSearchConstants#CAST_TYPE_REFERENCE CAST_TYPE_REFERENCE} * <td>Return only type references used in a cast expression. * <tr> * <td>{@link IJavaSearchConstants#CATCH_TYPE_REFERENCE CATCH_TYPE_REFERENCE} * <td>Return only type references used in a catch header. * <tr> * <td>{@link IJavaSearchConstants#CLASS_INSTANCE_CREATION_TYPE_REFERENCE * CLASS_INSTANCE_CREATION_TYPE_REFERENCE} * <td>Return only type references used in class instance creation. * <tr> * <td>{@link IJavaSearchConstants#RETURN_TYPE_REFERENCE RETURN_TYPE_REFERENCE} * <td>Return only type references used as a method return type. * <tr> * <td>{@link IJavaSearchConstants#IMPORT_DECLARATION_TYPE_REFERENCE * IMPORT_DECLARATION_TYPE_REFERENCE} * <td>Return only type references used in an import declaration. * <tr> * <td>{@link IJavaSearchConstants#ANNOTATION_TYPE_REFERENCE * ANNOTATION_TYPE_REFERENCE} * <td>Return only type references used as an annotation. * <tr> * <td>{@link IJavaSearchConstants#TYPE_ARGUMENT_TYPE_REFERENCE * TYPE_ARGUMENT_TYPE_REFERENCE} * <td>Return only type references used as a type argument in a parameterized type or * a parameterized method. * <tr> * <td>{@link IJavaSearchConstants#TYPE_VARIABLE_BOUND_TYPE_REFERENCE * TYPE_VARIABLE_BOUND_TYPE_REFERENCE} * <td>Return only type references used as a type variable bound. * <tr> * <td>{@link IJavaSearchConstants#WILDCARD_BOUND_TYPE_REFERENCE * WILDCARD_BOUND_TYPE_REFERENCE} * <td>Return only type references used as a wildcard bound. * <tr> * <td>{@link IJavaSearchConstants#INSTANCEOF_TYPE_REFERENCE * INSTANCEOF_TYPE_REFERENCE} * <td>Return only type references used as a type of an <code>instanceof</code> * expression. * <tr> * <td>{@link IJavaSearchConstants#SUPER_REFERENCE SUPER_REFERENCE} * <td>Return only super field accesses or super method invocations (e.g. using the * <code>super</code> qualifier). * <tr> * <td>{@link IJavaSearchConstants#QUALIFIED_REFERENCE QUALIFIED_REFERENCE} * <td>Return only qualified field accesses or qualified method invocations. * <tr> * <td>{@link IJavaSearchConstants#THIS_REFERENCE THIS_REFERENCE} * <td>Return only primary field accesses or primary method invocations (e.g. using * the <code>this</code> qualifier). * <tr> * <td>{@link IJavaSearchConstants#IMPLICIT_THIS_REFERENCE IMPLICIT_THIS_REFERENCE} * <td>Return only field accesses or method invocations without any qualification. * </table> * </li> * </ul> * @return a search pattern for a Java element or <code>null</code> if the given element is * ill-formed */ public static SearchPattern createPattern(IJavaElement element, int limitTo) { return createPattern(element, limitTo, R_EXACT_MATCH | R_CASE_SENSITIVE | R_ERASURE_MATCH); } /** * Returns a search pattern based on a given Java element. The pattern is used to trigger the * appropriate search, and can be parameterized as follows: * * @param element the Java element the search pattern is based on * @param limitTo determines the nature of the expected matches * <ul> * <li>{@link IJavaSearchConstants#DECLARATIONS DECLARATIONS}: will search * declarations matching with the corresponding element. In case the element is a * method, declarations of matching methods in sub-types will also be found, allowing * to find declarations of abstract methods, etc. Some additional flags may be * specified while searching declaration: * <ul> * <li>{@link IJavaSearchConstants#IGNORE_DECLARING_TYPE 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 IJavaSearchConstants#IGNORE_RETURN_TYPE 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 IJavaSearchConstants#REFERENCES REFERENCES}: will search references to * the given element.</li> * <li>{@link IJavaSearchConstants#ALL_OCCURRENCES ALL_OCCURRENCES}: will search for * either declarations or references as specified above.</li> * <li>All other fine grain constants defined in the <b>limitTo</b> category of the * {@link IJavaSearchConstants} are also accepted nature: * <table border=0> * <tr> * <th align=left>Fine grain constant * <th align=left>Meaning * <tr> * <td>{@link IJavaSearchConstants#FIELD_DECLARATION_TYPE_REFERENCE * FIELD_DECLARATION_TYPE_REFERENCE} * <td>Return only type references used as the type of a field declaration. * <tr> * <td>{@link IJavaSearchConstants#LOCAL_VARIABLE_DECLARATION_TYPE_REFERENCE * LOCAL_VARIABLE_DECLARATION_TYPE_REFERENCE} * <td>Return only type references used as the type of a local variable declaration. * <tr> * <td>{@link IJavaSearchConstants#PARAMETER_DECLARATION_TYPE_REFERENCE * PARAMETER_DECLARATION_TYPE_REFERENCE} * <td>Return only type references used as the type of a method parameter * declaration. * <tr> * <td>{@link IJavaSearchConstants#SUPERTYPE_TYPE_REFERENCE SUPERTYPE_TYPE_REFERENCE} * <td>Return only type references used as a super type or as a super interface. * <tr> * <td>{@link IJavaSearchConstants#THROWS_CLAUSE_TYPE_REFERENCE * THROWS_CLAUSE_TYPE_REFERENCE} * <td>Return only type references used in a throws clause. * <tr> * <td>{@link IJavaSearchConstants#CAST_TYPE_REFERENCE CAST_TYPE_REFERENCE} * <td>Return only type references used in a cast expression. * <tr> * <td>{@link IJavaSearchConstants#CATCH_TYPE_REFERENCE CATCH_TYPE_REFERENCE} * <td>Return only type references used in a catch header. * <tr> * <td>{@link IJavaSearchConstants#CLASS_INSTANCE_CREATION_TYPE_REFERENCE * CLASS_INSTANCE_CREATION_TYPE_REFERENCE} * <td>Return only type references used in class instance creation. * <tr> * <td>{@link IJavaSearchConstants#RETURN_TYPE_REFERENCE RETURN_TYPE_REFERENCE} * <td>Return only type references used as a method return type. * <tr> * <td>{@link IJavaSearchConstants#IMPORT_DECLARATION_TYPE_REFERENCE * IMPORT_DECLARATION_TYPE_REFERENCE} * <td>Return only type references used in an import declaration. * <tr> * <td>{@link IJavaSearchConstants#ANNOTATION_TYPE_REFERENCE * ANNOTATION_TYPE_REFERENCE} * <td>Return only type references used as an annotation. * <tr> * <td>{@link IJavaSearchConstants#TYPE_ARGUMENT_TYPE_REFERENCE * TYPE_ARGUMENT_TYPE_REFERENCE} * <td>Return only type references used as a type argument in a parameterized type or * a parameterized method. * <tr> * <td>{@link IJavaSearchConstants#TYPE_VARIABLE_BOUND_TYPE_REFERENCE * TYPE_VARIABLE_BOUND_TYPE_REFERENCE} * <td>Return only type references used as a type variable bound. * <tr> * <td>{@link IJavaSearchConstants#WILDCARD_BOUND_TYPE_REFERENCE * WILDCARD_BOUND_TYPE_REFERENCE} * <td>Return only type references used as a wildcard bound. * <tr> * <td>{@link IJavaSearchConstants#INSTANCEOF_TYPE_REFERENCE * INSTANCEOF_TYPE_REFERENCE} * <td>Return only type references used as a type of an <code>instanceof</code> * expression. * <tr> * <td>{@link IJavaSearchConstants#SUPER_REFERENCE SUPER_REFERENCE} * <td>Return only super field accesses or super method invocations (e.g. using the * <code>super</code> qualifier). * <tr> * <td>{@link IJavaSearchConstants#QUALIFIED_REFERENCE QUALIFIED_REFERENCE} * <td>Return only qualified field accesses or qualified method invocations. * <tr> * <td>{@link IJavaSearchConstants#THIS_REFERENCE THIS_REFERENCE} * <td>Return only primary field accesses or primary method invocations (e.g. using * the <code>this</code> qualifier). * <tr> * <td>{@link IJavaSearchConstants#IMPLICIT_THIS_REFERENCE IMPLICIT_THIS_REFERENCE} * <td>Return only field accesses or method invocations without any qualification. * </table> * </li> * </ul> * @param matchRule one of the following match rules: * <ul> * <li>{@link #R_EXACT_MATCH}</li> * <li>{@link #R_PREFIX_MATCH}</li> * <li>{@link #R_PATTERN_MATCH}</li> * <li>{@link #R_REGEXP_MATCH}</li> * <li>{@link #R_CAMELCASE_MATCH}</li> * <li>{@link #R_CAMELCASE_SAME_PART_COUNT_MATCH}</li> * </ul> * , which may be also combined with one of the following flags: * <ul> * <li>{@link #R_CASE_SENSITIVE}</li> * <li>{@link #R_ERASURE_MATCH}</li> * <li>{@link #R_EQUIVALENT_MATCH}</li> * </ul> * For example, * <ul> * <li>{@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE}: if an exact and case * sensitive match is requested,</li> * <li>{@link #R_PREFIX_MATCH} if a case insensitive prefix match is requested</li> * <li>{@link #R_EXACT_MATCH} | {@link #R_ERASURE_MATCH}: if a case insensitive and * erasure match is requested.</li> * </ul> * Note that {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH} has no effect on * non-generic types/methods search. * <p> * Note also that default behavior for generic types/methods search is to find exact * matches. * @return a search pattern for a Java element or <code>null</code> if the given element is * ill-formed * @since 3.1 */ public static SearchPattern createPattern(IJavaElement element, int limitTo, int matchRule) { SearchPattern searchPattern= null; int lastDot; boolean ignoreDeclaringType= false; boolean ignoreReturnType= false; int maskedLimitTo= limitTo & ~(IJavaSearchConstants.IGNORE_DECLARING_TYPE + IJavaSearchConstants.IGNORE_RETURN_TYPE); if (maskedLimitTo == IJavaSearchConstants.DECLARATIONS || maskedLimitTo == IJavaSearchConstants.ALL_OCCURRENCES) { ignoreDeclaringType= (limitTo & IJavaSearchConstants.IGNORE_DECLARING_TYPE) != 0; ignoreReturnType= (limitTo & IJavaSearchConstants.IGNORE_RETURN_TYPE) != 0; } if ((matchRule= validateMatchRule(null, matchRule)) == -1) { return null; } char[] declaringSimpleName= null; char[] declaringQualification= null; switch (element.getElementType()) { case IJavaElement.FIELD: IField field= (IField)element; if (!ignoreDeclaringType) { IType declaringClass= field.getDeclaringType(); declaringSimpleName= declaringClass.getElementName().toCharArray(); declaringQualification= declaringClass.getPackageFragment().getElementName().toCharArray(); char[][] enclosingNames= enclosingTypeNames(declaringClass); 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.getTypeErasure(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 (JavaModelException e) { return null; } } // Create field pattern searchPattern= new FieldPattern( name, declaringQualification, declaringSimpleName, typeQualification, typeSimpleName, typeSignature, limitTo, matchRule); break; case IJavaElement.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 IJavaElement.LOCAL_VARIABLE: LocalVariable localVar= (LocalVariable)element; searchPattern= new LocalVariablePattern(localVar, limitTo, matchRule); break; case IJavaElement.TYPE_PARAMETER: ITypeParameter typeParam= (ITypeParameter)element; boolean findParamDeclarations= true; boolean findParamReferences= true; switch (maskedLimitTo) { case IJavaSearchConstants.DECLARATIONS: findParamReferences= false; break; case IJavaSearchConstants.REFERENCES: findParamDeclarations= false; break; } searchPattern= new TypeParameterPattern( findParamDeclarations, findParamReferences, typeParam, matchRule); break; case IJavaElement.METHOD: IMethod method= (IMethod)element; boolean isConstructor; try { isConstructor= method.isConstructor(); } catch (JavaModelException e) { return null; } IType declaringClass= method.getDeclaringType(); 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, '.'), '.'); } } 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.getTypeErasure(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 (JavaModelException 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.getTypeErasure(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 if (isConstructor) { searchPattern= new ConstructorPattern( declaringSimpleName, declaringQualification, parameterQualifications, parameterSimpleNames, parameterSignatures, method, limitTo, matchRule); } else { searchPattern= new MethodPattern( selector, declaringQualification, declaringSimpleName, returnQualification, returnSimpleName, returnSignature, parameterQualifications, parameterSimpleNames, parameterSignatures, method, limitTo, matchRule); } break; case IJavaElement.TYPE: IType type= (IType)element; searchPattern= createTypePattern( type.getElementName().toCharArray(), type.getPackageFragment().getElementName().toCharArray(), ignoreDeclaringType ? null : enclosingTypeNames(type), null, type, maskedLimitTo, matchRule); break; case IJavaElement.PACKAGE_DECLARATION: case IJavaElement.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 IJavaSearchConstants.DECLARATIONS: return new TypeDeclarationPattern( packageName, enclosingTypeNames, simpleName, IIndexConstants.TYPE_SUFFIX, matchRule); case IJavaSearchConstants.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 IJavaSearchConstants.IMPLEMENTORS: return new SuperTypeReferencePattern( CharOperation.concatWith(packageName, enclosingTypeNames, '.'), simpleName, SuperTypeReferencePattern.ONLY_SUPER_INTERFACES, matchRule); case IJavaSearchConstants.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)); default: if (type != null) { return new TypeReferencePattern( CharOperation.concatWith(packageName, enclosingTypeNames, '.'), simpleName, type, limitTo, matchRule); } } return null; } 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$ - fall through default case to add token to type default: // all other tokens are considered identifiers (see bug 21763 Problem in Java 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); if (typeSignature.indexOf(Signature.C_GENERIC_START) < 0) { typePart= type.toCharArray(); } else { typePart= Signature.toCharArray(Signature.getTypeErasure(typeSignature.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 IJavaSearchConstants.DECLARATIONS: // cannot search for explicit member types return new QualifiedTypeDeclarationPattern(qualificationChars, typeChars, indexSuffix, matchRule); case IJavaSearchConstants.REFERENCES: return new TypeReferencePattern(qualificationChars, typeChars, typeSignature, indexSuffix, matchRule); case IJavaSearchConstants.IMPLEMENTORS: return new SuperTypeReferencePattern(qualificationChars, typeChars, SuperTypeReferencePattern.ONLY_SUPER_INTERFACES, indexSuffix, matchRule); case IJavaSearchConstants.ALL_OCCURRENCES: return new OrPattern( new QualifiedTypeDeclarationPattern(qualificationChars, typeChars, indexSuffix, matchRule),// cannot search for explicit member types new TypeReferencePattern(qualificationChars, typeChars, typeSignature, indexSuffix, matchRule)); default: return new TypeReferencePattern(qualificationChars, typeChars, typeSignature, limitTo, indexSuffix, matchRule); } } /** * Returns the enclosing type names of the given type. */ private static char[][] enclosingTypeNames(IType type) { IJavaElement parent= type.getParent(); switch (parent.getElementType()) { case IJavaElement.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 IJavaElement.COMPILATION_UNIT: return CharOperation.NO_CHAR_CHAR; case IJavaElement.FIELD: case IJavaElement.INITIALIZER: case IJavaElement.METHOD: IType declaringClass= ((IMember)parent).getDeclaringType(); return CharOperation.arrayConcat( enclosingTypeNames(declaringClass), new char[][] { declaringClass.getElementName().toCharArray(), IIndexConstants.ONE_STAR }); case IJavaElement.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 } /** * Query a given index for matching entries. Assumes the sender has opened the index and will * close when finished. * * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. */ public void findIndexMatches(Index index, IndexQueryRequestor requestor, SearchParticipant participant, IJavaSearchScope scope, IProgressMonitor monitor) throws IOException { if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException(); try { index.startQuery(); SearchPattern pattern= currentPattern(); EntryResult[] entries= pattern.queryIn(index); if (entries == null) return; SearchPattern decodedResult= pattern.getBlankPattern(); String containerPath= index.containerPath; char separator= index.separator; for (int i= 0, l= entries.length; i < l; i++) { if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException(); EntryResult entry= entries[i]; decodedResult.decodeIndexKey(entry.getWord()); if (pattern.matchesDecodedKey(decodedResult)) { // TODO (kent) some clients may not need the document names String[] names= entry.getDocumentNames(index); for (int j= 0, n= names.length; j < n; j++) acceptMatch(names[j], containerPath, separator, decodedResult, requestor, participant, scope, monitor); } } } finally { index.stopQuery(); } } /** * 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; } /** * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. */ public boolean isPolymorphicSearch() { return false; } /** * 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; int matchMode= this.matchRule & MODE_MASK; boolean emptyPattern= pattern.length == 0; if (emptyPattern && (this.matchRule & R_PREFIX_MATCH) != 0) 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]); switch (matchMode) { case R_EXACT_MATCH: if (sameLength && matchFirstChar) { return CharOperation.equals(pattern, name, isCaseSensitive); } break; 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 SearchPattern.R_CAMELCASE_MATCH: if (matchFirstChar && CharOperation.camelCaseMatch(pattern, name, false)) { return true; } // only test case insensitive as CamelCase already verified prefix case sensitive if (!isCaseSensitive && matchFirstChar && CharOperation.prefixEquals(pattern, name, false)) { return true; } break; case SearchPattern.R_CAMELCASE_SAME_PART_COUNT_MATCH: return matchFirstChar && CharOperation.camelCaseMatch(pattern, name, true); case R_REGEXP_MATCH: // TODO implement regular expression match return true; } } return false; } /** * Validate compatibility between given string pattern and match rule. <br> * In certain circumstances described in the table below, the returned match rule is modified in * order to provide a more efficient search pattern: * <ol> * <li>when the {@link #R_REGEXP_MATCH} flag is set, then <b>the pattern is rejected</b> as this * kind of match is not supported yet and <code>-1</code> is returned).</li> * <li>when the string pattern has <u>no</u> pattern characters (e.g. '*' or '?') and the * pattern match flag is set (i.e. the match rule has the {@link #R_PATTERN_MATCH} flag), then * <b>the pattern match flag is reset</b>.<br> * Reversely, when the string pattern has pattern characters and the pattern match flag is * <u>not</u> set, then <b>the pattern match flag is set</b>.</li> * <li>when the {@link #R_PATTERN_MATCH} flag is set then, <b>other {@link #R_PREFIX_MATCH}, * {@link #R_CAMELCASE_MATCH} or {@link #R_CAMELCASE_SAME_PART_COUNT_MATCH} flags are reset</b> * if they are tentatively combined.</li> * <li>when the {@link #R_CAMELCASE_MATCH} flag is set, then <b>other {@link #R_PREFIX_MATCH} or * {@link #R_CAMELCASE_SAME_PART_COUNT_MATCH} flags are reset</b> if they are tentatively * combined.<br> * Reversely, if the string pattern cannot be a camel case pattern (i.e. contains invalid Java * identifier characters or does not have at least two uppercase characters - one for method * camel case patterns), then <b>the CamelCase match flag is replaced with a prefix match * flag</b>.</li> * <li>when the {@link #R_CAMELCASE_SAME_PART_COUNT_MATCH} flag is set, then <b>( * {@link #R_PREFIX_MATCH} flag is reset</b> if it's tentatively combined.<br> * Reversely, if the string pattern cannot be a camel case pattern (i.e. contains invalid Java * identifier characters or does not have at least two uppercase characters - one for method * camel case patterns), then <b>the CamelCase part count match flag is reset</b>.</li> * </ol> * <i>Note: the rules are validated in the documented order. For example, it means that as soon * as the string pattern contains one pattern character, the pattern match flag will be set and * all other match flags reset: validation of rule 2) followed by rule 3)...</i> * <p> * * @param stringPattern The string pattern * @param matchRule The match rule * @return Optimized valid match rule or -1 if an incompatibility was detected. * @since 3.2 */ 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 || (matchRule & R_CAMELCASE_SAME_PART_COUNT_MATCH) != 0) { // regexp is not supported yet return -1; } } // Verify Pattern match rule if (stringPattern != null) { int starIndex= stringPattern.indexOf('*'); int questionIndex= stringPattern.indexOf('?'); if (starIndex < 0 && questionIndex < 0) { // reset pattern match flag if any matchRule&= ~R_PATTERN_MATCH; } else { // force Pattern rule matchRule|= R_PATTERN_MATCH; } } if ((matchRule & R_PATTERN_MATCH) != 0) { // reset other incompatible flags matchRule&= ~R_CAMELCASE_MATCH; matchRule&= ~R_CAMELCASE_SAME_PART_COUNT_MATCH; matchRule&= ~R_PREFIX_MATCH; return matchRule; } // Verify Camel Case if ((matchRule & R_CAMELCASE_MATCH) != 0) { // reset other incompatible flags matchRule&= ~R_CAMELCASE_SAME_PART_COUNT_MATCH; matchRule&= ~R_PREFIX_MATCH; // validate camel case rule and modify it if not valid boolean validCamelCase= validateCamelCasePattern(stringPattern); if (!validCamelCase) { matchRule&= ~R_CAMELCASE_MATCH; matchRule|= R_PREFIX_MATCH; } return matchRule; } // Verify Camel Case with same count of parts if ((matchRule & R_CAMELCASE_SAME_PART_COUNT_MATCH) != 0) { // reset other incompatible flags matchRule&= ~R_PREFIX_MATCH; // validate camel case rule and modify it if not valid boolean validCamelCase= validateCamelCasePattern(stringPattern); if (!validCamelCase) { matchRule&= ~R_CAMELCASE_SAME_PART_COUNT_MATCH; } return matchRule; } // Return the validated match rule (modified if necessary) return matchRule; } /* * Validate pattern for a camel case match rule * @return */ private static boolean validateCamelCasePattern(String stringPattern) { if (stringPattern == null) return true; // verify sting pattern validity int length= stringPattern.length(); boolean validCamelCase= true; boolean lowerCamelCase= false; int uppercase= 0; for (int i= 0; i < length && validCamelCase; i++) { char ch= stringPattern.charAt(i); validCamelCase= i == 0 ? ScannerHelper.isJavaIdentifierStart(ch) : ScannerHelper.isJavaIdentifierPart(ch); // at least one uppercase character is need in CamelCase pattern // (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=136313) if (ScannerHelper.isUpperCase(ch)) uppercase++; if (i == 0) lowerCamelCase= uppercase == 0; } if (validCamelCase) { validCamelCase= lowerCamelCase ? uppercase > 0 : uppercase > 1; } return validCamelCase; } /** * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. */ public EntryResult[] queryIn(Index index) throws IOException { return index.query(getIndexCategories(), getIndexKey(), getMatchRule()); } /** * @see java.lang.Object#toString() */ public String toString() { return "SearchPattern"; //$NON-NLS-1$ } }