/******************************************************************************* * Copyright (c) 2000, 2007 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 * *******************************************************************************/ package org.eclipse.dltk.core.search; import static org.eclipse.dltk.core.search.indexing.IIndexConstants.TYPE_SEPARATOR; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.eclipse.dltk.compiler.CharOperation; import org.eclipse.dltk.compiler.util.ScannerHelper; import org.eclipse.dltk.core.DLTKLanguageManager; import org.eclipse.dltk.core.IDLTKLanguageToolkit; import org.eclipse.dltk.core.ILocalVariable; import org.eclipse.dltk.core.IMember; import org.eclipse.dltk.core.IMethod; import org.eclipse.dltk.core.IModelElement; import org.eclipse.dltk.core.INamespace; import org.eclipse.dltk.core.ISearchPatternProcessor; import org.eclipse.dltk.core.ISearchPatternProcessor.ITypePattern; import org.eclipse.dltk.core.IType; import org.eclipse.dltk.core.ModelException; import org.eclipse.dltk.core.search.indexing.IIndexConstants; import org.eclipse.dltk.core.search.matching.MatchLocator; import org.eclipse.dltk.internal.core.search.matching.FieldPattern; import org.eclipse.dltk.internal.core.search.matching.InternalSearchPattern; import org.eclipse.dltk.internal.core.search.matching.LocalVariablePattern; import org.eclipse.dltk.internal.core.search.matching.MethodDeclarationPattern; import org.eclipse.dltk.internal.core.search.matching.MethodPattern; import org.eclipse.dltk.internal.core.search.matching.OrPattern; import org.eclipse.dltk.internal.core.search.matching.QualifiedTypeDeclarationPattern; import org.eclipse.dltk.internal.core.search.matching.TypeDeclarationPattern; import org.eclipse.dltk.internal.core.search.matching.TypeReferencePattern; /** * A search pattern defines how search results are found. Use * <code>SearchPattern.createPattern</code> to create a search pattern. * <p> * Search patterns are used during the search phase to decode index entries that * were added during the indexing phase (see * {@link SearchDocument#addIndexEntry(char[], char[])}). When an index is * queried, the index categories and keys to consider are retrieved from the * search pattern using {@link #getIndexCategories()} and {@link #getIndexKey()} * , as well as the match rule (see {@link #getMatchRule()}). A blank pattern is * then created (see {@link #getBlankPattern()}). This blank pattern is used as * a record as follows. For each index entry in the given index categories and * that starts with the given key, the blank pattern is fed using * {@link #decodeIndexKey(char[])}. The original pattern is then asked if it * matches the decoded key using {@link #matchesDecodedKey(SearchPattern)}. If * it matches, a search doument is created for this index entry using * {@link SearchParticipant#getDocument(String)}. * * </p> * <p> * This class is intended to be subclassed by clients. A default behavior is * provided for each of the methods above, that clients can ovveride if they * wish. * </p> * */ public abstract class SearchPattern extends InternalSearchPattern { // Rules for pattern matching: (exact, prefix, pattern) [ | case sensitive] /** * Match rule: The search pattern matches exactly the search result, that * is, the source of the search result equals the search pattern. */ public static final int R_EXACT_MATCH = 0; /** * Match rule: The search pattern is a prefix of the search result. */ public static final int R_PREFIX_MATCH = 0x0001; /** * Match rule: The search pattern contains one or more wild cards ('*' or * '?'). A '*' wild-card can replace 0 or more characters in the search * result. A '?' wild-card replaces exactly 1 character in the search * result. */ public static final int R_PATTERN_MATCH = 0x0002; /** * Match rule: The search pattern contains a regular expression. */ public static final int R_REGEXP_MATCH = 0x0004; /** * Match rule: The search pattern matches the search result only if cases * are the same. Can be combined to previous rules, e.g. * {@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE} */ public static final int R_CASE_SENSITIVE = 0x0008; /** * Match rule: The search pattern matches search results as * raw/parameterized types/methods with same erasure. This mode has no * effect on otherscriptelements search.<br> * Type search example: * <ul> * <li>pattern: <code>List<Exception></code></li> * <li>match: <code>List<Object></code></li> * </ul> * Method search example: * <ul> * <li>declaration: <code><T>foo(T t)</code></li> * <li>pattern: <code><Exception>foo(new Exception())</code></li> * <li>match: <code><Object>foo(new Object())</code></li> * </ul> * Can be combined to all other match rules, e.g. {@link #R_CASE_SENSITIVE} * | {@link #R_ERASURE_MATCH} This rule is not activated by default, so raw * types or parameterized types with same erasure will not be found for * pattern List<String>, Note that with this pattern, the match * selection will be only on the erasure even for parameterized types. * * */ public static final int R_ERASURE_MATCH = 0x0010; /** * Match rule: The search pattern matches search results as * raw/parameterized types/methods with equivalent type parameters. This * mode has no effect on otherscriptelements search.<br> * Type search example: * <ul> * <li>pattern: <code>List<Exception></code></li> * <li>match: * <ul> * <li><code>List<? extends Throwable></code></li> * <li><code>List<? super RuntimeException></code></li> * <li><code>List<?></code></li> * </ul> * </li> * </ul> * Method search example: * <ul> * <li>declaration: <code><T>foo(T t)</code></li> * <li>pattern: <code><Exception>foo(new Exception())</code></li> * <li>match: * <ul> * <li><code><? extends Throwable>foo(new Exception())</code></li> * <li><code><? super RuntimeException>foo(new Exception())</code></li> * <li><code>foo(new Exception())</code></li> * </ul> * </ul> * Can be combined to all other match rules, e.g. {@link #R_CASE_SENSITIVE} * | {@link #R_EQUIVALENT_MATCH} This rule is not activated by default, so * raw types or equivalent parameterized types will not be found for pattern * List<String>, This mode is overridden by {@link #R_ERASURE_MATCH} * as erasure matches obviously include equivalent ones. That means that * pattern with rule set to {@link #R_EQUIVALENT_MATCH} | * {@link #R_ERASURE_MATCH} will return same results than rule only set with * {@link #R_ERASURE_MATCH}. * * */ public static final int R_EQUIVALENT_MATCH = 0x0020; /** * Match rule: The search pattern matches exactly the search result, that * is, the source of the search result equals the search pattern. * * */ public static final int R_FULL_MATCH = 0x0040; /** * Match rule: The search pattern contains a Camel Case expression. <br> * Examples: * <ul> * <li><code>NPE</code> type string pattern will match * <code>NullPointerException</code> and <code>NpPermissionException</code> * types,</li> * <li><code>NuPoEx</code> type string pattern will only match * <code>NullPointerException</code> type.</li> * </ul> * * @see CharOperation#camelCaseMatch(char[], char[]) for a detailed * explanation of Camel Case matching. <br> * Can be combined to {@link #R_PREFIX_MATCH} match rule. For example, * when prefix match rule is combined with Camel Case match rule, * <code>"nPE"</code> pattern will match <code>nPException</code>. <br> * Match rule {@link #R_PATTERN_MATCH} may also be combined but both * rules will not be used simultaneously as they are mutually * exclusive. Used match rule depends on whether string pattern * contains specific pattern characters (e.g. '*' or '?') or not. If it * does, then only Pattern match rule will be used, otherwise only * Camel Case match will be used. For example, with <code>"NPE"</code> * string pattern, search will only use Camel Case match rule, but with * <code>N*P*E*</code> string pattern, it will use only Pattern match * rule. * * */ public static final int R_CAMELCASE_MATCH = 0x0080; private static final int MODE_MASK = R_EXACT_MATCH | R_PREFIX_MATCH | R_PATTERN_MATCH | R_REGEXP_MATCH; private int matchRule; private IDLTKLanguageToolkit toolkit; private Pattern regexpCompiledPattern; /** * Creates a search pattern with the rule to apply for matching index keys. * It can be exact match, prefix match, pattern match or regexp match. Rule * can also be combined with a case sensitivity flag. * * @param matchRule * one of {@link #R_EXACT_MATCH}, {@link #R_PREFIX_MATCH}, * {@link #R_PATTERN_MATCH}, {@link #R_REGEXP_MATCH}, * {@link #R_CAMELCASE_MATCH} combined with one of following * values: {@link #R_CASE_SENSITIVE}, {@link #R_ERASURE_MATCH} or * {@link #R_EQUIVALENT_MATCH}. e.g. {@link #R_EXACT_MATCH} | * {@link #R_CASE_SENSITIVE} if an exact and case sensitive match * is requested, {@link #R_PREFIX_MATCH} if a prefix non case * sensitive match is requested or {@link #R_EXACT_MATCH} | * {@link #R_ERASURE_MATCH} if a non case sensitive and erasure * match is requested.<br> * Note that {@link #R_ERASURE_MATCH} or * {@link #R_EQUIVALENT_MATCH} have no effect on non-generic * types/methods search.<br> * Note also that default behavior for generic types/methods * search is to find exact matches. */ public SearchPattern(int matchRule, IDLTKLanguageToolkit toolkit) { this.matchRule = matchRule; this.toolkit = toolkit; // Set full match implicit mode if ((matchRule & (R_EQUIVALENT_MATCH | R_ERASURE_MATCH)) == 0) { this.matchRule |= R_FULL_MATCH; } } public IDLTKLanguageToolkit getToolkit() { return this.toolkit; } /** * Answers true if the pattern matches the given name using CamelCase rules, * or false otherwise. CamelCase matching does NOT accept explicit * wild-cards '*' and '?' and is inherently case sensitive. <br> * CamelCase denotes the convention of writing compound names without * spaces, and capitalizing every term. This function recognizes both upper * and lower CamelCase, depending whether the leading character is * capitalized or not. The leading part of an upper CamelCase pattern is * assumed to contain a sequence of capitals which are appearing in the * matching name; e.g. 'NPE' will match 'NullPointerException', but not * 'NewPerfData'. A lower CamelCase pattern uses a lowercase first * character. In Script, type names follow the upper CamelCase convention, * whereas method or field names follow the lower CamelCase convention. <br> * The pattern may contain lowercase characters, which will be match in a * case sensitive way. These characters must appear in sequence in the name. * For instance, 'NPExcep' will match 'NullPointerException', but not * 'NullPointerExCEPTION' or 'NuPoEx' will match 'NullPointerException', but * not 'NoPointerException'. <br> * <br> * Examples: * <ol> * <li> * * <pre> * pattern = "NPE" * name = NullPointerException / NoPermissionException * result => true * </pre> * * </li> * <li> * * <pre> * pattern = "NuPoEx" * name = NullPointerException * result => true * </pre> * * </li> * <li> * * <pre> * pattern = "npe" * name = NullPointerException * result => false * </pre> * * </li> * </ol> * * @see CharOperation#camelCaseMatch(char[], char[]) Implementation has been * entirely copied from this method except for array lengthes which * were obviously replaced with calls to {@link String#length()}. * * @param pattern * the given pattern * @param name * the given name * @return true if the pattern matches the given name, false otherwise * */ public static final boolean camelCaseMatch(String pattern, String name) { if (pattern == null) return true; // null pattern is equivalent to '*' if (name == null) return false; // null name cannot match return camelCaseMatch(pattern, 0, pattern.length(), name, 0, name.length()); } /** * Answers true if a sub-pattern matches the subpart of the given name using * CamelCase rules, or false otherwise. CamelCase matching does NOT accept * explicit wild-cards '*' and '?' and is inherently case sensitive. Can * match only subset of name/pattern, considering end positions as * non-inclusive. The subpattern is defined by the patternStart and * patternEnd positions. <br> * CamelCase denotes the convention of writing compound names without * spaces, and capitalizing every term. This function recognizes both upper * and lower CamelCase, depending whether the leading character is * capitalized or not. The leading part of an upper CamelCase pattern is * assumed to contain a sequence of capitals which are appearing in the * matching name; e.g. 'NPE' will match 'NullPointerException', but not * 'NewPerfData'. A lower CamelCase pattern uses a lowercase first * character. In Script, type names follow the upper CamelCase convention, * whereas method or field names follow the lower CamelCase convention. <br> * The pattern may contain lowercase characters, which will be match in a * case sensitive way. These characters must appear in sequence in the name. * For instance, 'NPExcep' will match 'NullPointerException', but not * 'NullPointerExCEPTION' or 'NuPoEx' will match 'NullPointerException', but * not 'NoPointerException'. <br> * <br> * Examples: * <ol> * <li> * * <pre> * pattern = "NPE" * patternStart = 0 * patternEnd = 3 * name = NullPointerException * nameStart = 0 * nameEnd = 20 * result => true * </pre> * * </li> * <li> * * <pre> * pattern = "NPE" * patternStart = 0 * patternEnd = 3 * name = NoPermissionException * nameStart = 0 * nameEnd = 21 * result => true * </pre> * * </li> * <li> * * <pre> * pattern = "NuPoEx" * patternStart = 0 * patternEnd = 6 * name = NullPointerException * nameStart = 0 * nameEnd = 20 * result => true * </pre> * * </li> * <li> * * <pre> * pattern = "NuPoEx" * patternStart = 0 * patternEnd = 6 * name = NoPermissionException * nameStart = 0 * nameEnd = 21 * result => false * </pre> * * </li> * <li> * * <pre> * pattern = "npe" * patternStart = 0 * patternEnd = 3 * name = NullPointerException * nameStart = 0 * nameEnd = 20 * result => false * </pre> * * </li> * </ol> * * @see CharOperation#camelCaseMatch(char[], int, int, char[], int, int) * Implementation has been entirely copied from this method except for * array lengthes which were obviously replaced with calls to * {@link String#length()} and for array direct access which were * replaced with calls to {@link String#charAt(int)}. * * @param pattern * the given pattern * @param patternStart * the start index of the pattern, inclusive * @param patternEnd * the end index of the pattern, exclusive * @param name * the given name * @param nameStart * the start index of the name, inclusive * @param nameEnd * the end index of the name, exclusive * @return true if a sub-pattern matches the subpart of the given name, * false otherwise * */ public static final boolean camelCaseMatch(String pattern, int patternStart, int patternEnd, String name, int nameStart, int nameEnd) { if (name == null) return false; // null name cannot match if (pattern == null) return true; // null pattern is equivalent to '*' if (patternEnd < 0) patternEnd = pattern.length(); if (nameEnd < 0) nameEnd = name.length(); if (patternEnd <= patternStart) return nameEnd <= nameStart; if (nameEnd <= nameStart) return false; // check first pattern char if (name.charAt(nameStart) != pattern.charAt(patternStart)) { // first char must strictly match (upper/lower) return false; } char patternChar, nameChar; int iPattern = patternStart; int iName = nameStart; // Main loop is on pattern characters while (true) { iPattern++; iName++; if (iPattern == patternEnd) { // We have exhausted pattern, so it's a match return true; } if (iName == nameEnd) { // We have exhausted name (and not pattern), so it's not a match return false; } // For as long as we're exactly matching, bring it on (even if it's // a lower case character) if ((patternChar = pattern.charAt(iPattern)) == name.charAt(iName)) { continue; } // If characters are not equals, then it's not a match if // patternChar is lowercase if (patternChar < ScannerHelper.MAX_OBVIOUS) { if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[patternChar] & ScannerHelper.C_UPPER_LETTER) == 0) { return false; } } else if (Character.isJavaIdentifierPart(patternChar) && !Character.isUpperCase(patternChar)) { return false; } // patternChar is uppercase, so let's find the next uppercase in // name while (true) { if (iName == nameEnd) { // We have exhausted name (and not pattern), so it's not a // match return false; } nameChar = name.charAt(iName); if (nameChar < ScannerHelper.MAX_OBVIOUS) { if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[nameChar] & (ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_SPECIAL | ScannerHelper.C_DIGIT)) != 0) { // nameChar is lowercase iName++; // nameChar is uppercase... } else if (patternChar != nameChar) { // .. and it does not match patternChar, so it's not a // match return false; } else { // .. and it matched patternChar. Back to the big loop break; } } else if (Character.isJavaIdentifierPart(nameChar) && !Character.isUpperCase(nameChar)) { // nameChar is lowercase iName++; // nameChar is uppercase... } else if (patternChar != nameChar) { // .. and it does not match patternChar, so it's not a match return false; } else { // .. and it matched patternChar. Back to the big loop break; } } // At this point, either name has been exhausted, or it is at an // uppercase letter. // Since pattern is also at an uppercase letter } } /** * Returns a search pattern that combines the given two patterns into an * "and" pattern. The search result will match both the left pattern and the * right pattern. * * @param leftPattern * the left pattern * @param rightPattern * the right pattern * @return an "and" pattern */ public static SearchPattern createAndPattern(SearchPattern leftPattern, SearchPattern rightPattern) { // return MatchLocator.createAndPattern(leftPattern, rightPattern); return null; } /** * Field pattern are formed by [declaringType.]name[ type] e.g. * java.lang.String.serialVersionUID long field* */ private static SearchPattern createFieldPattern(String patternString, int limitTo, int matchRule, IDLTKLanguageToolkit toolkit) { // Signatures char[] declaringTypeSignature = null; // extract declaring type infos // extract parameter types infos // Create method/constructor pattern boolean findDeclarations = true; boolean findReferences = true; switch (limitTo) { case IDLTKSearchConstants.DECLARATIONS: findReferences = false; break; case IDLTKSearchConstants.REFERENCES: findDeclarations = false; break; case IDLTKSearchConstants.ALL_OCCURRENCES: break; } char[] selectorChars = patternString.toCharArray(); char declaringTypeQualification[] = null; char declaringTypeSimpleName[] = null; return new FieldPattern(findDeclarations, findReferences, findReferences, selectorChars, declaringTypeQualification, declaringTypeSimpleName, declaringTypeSignature, null, matchRule, toolkit); } /** * Method pattern are formed by:<br> * [declaringType '.'] ['<' typeArguments '>'] selector ['(' * parameterTypes ')'] [returnType] <br> * e.g. * <ul> * <li>java.lang.Runnable.run() void</li> * <li>main(*)</li> * <li><String>toArray(String[])</li> * </ul> * Constructor pattern are formed by:<br> * [declaringQualification '.'] ['<' typeArguments '>'] type ['(' * parameterTypes ')'] <br> * e.g. * <ul> * <li>java.lang.Object()</li> * <li>Main(*)</li> * <li><Exception>Sample(Exception)</li> * </ul> * Type arguments have the same pattern that for type patterns * * @param toolkit * TODO * */ private static SearchPattern createMethodOrConstructorPattern( String patternString, int limitTo, int matchRule, boolean isConstructor, IDLTKLanguageToolkit toolkit) { // Signatures String declaringTypeSignature = null; char declaringTypeQualification[] = null; char declaringTypeSimpleName[] = null; char[] selectorChars; // extract declaring type infos // extract parameter types infos // Create method/constructor pattern ISearchPatternProcessor processor = DLTKLanguageManager .getSearchPatternProcessor(toolkit); if (processor != null) { declaringTypeQualification = processor .extractDeclaringTypeQualification(patternString); declaringTypeSimpleName = processor .extractDeclaringTypeSimpleName(patternString); selectorChars = processor.extractSelector(patternString); } else { selectorChars = patternString.toCharArray(); } boolean findDeclarations = true; boolean findReferences = true; switch (limitTo) { case IDLTKSearchConstants.DECLARATIONS: findReferences = false; break; case IDLTKSearchConstants.REFERENCES: findDeclarations = false; break; case IDLTKSearchConstants.ALL_OCCURRENCES: break; } if (isConstructor) { // return new ConstructorPattern(findDeclarations, findReferences, // declaringTypeSimpleName, declaringTypeQualification, // declaringTypeSignature, parameterTypeQualifications, // parameterTypeSimpleNames, parameterTypeSignatures, typeArguments, // matchRule); } else { MethodDeclarationPattern declarationPattern = null; MethodPattern referencesPattern = null; if (findDeclarations) { final char[][] packageNames; if (declaringTypeQualification != null) { packageNames = CharOperation.splitOn(processor .getDelimiterReplacementString().toCharArray(), declaringTypeQualification); } else { packageNames = CharOperation.NO_CHAR_CHAR; } int enclosingTypeCount = packageNames.length; if (declaringTypeSimpleName != null) { ++enclosingTypeCount; } final char[][] enclosingTypes; if (enclosingTypeCount != 0) { enclosingTypes = new char[enclosingTypeCount][]; System.arraycopy(packageNames, 0, enclosingTypes, 0, packageNames.length); if (declaringTypeSimpleName != null) { enclosingTypes[enclosingTypeCount - 1] = declaringTypeSimpleName; } } else { enclosingTypes = null; } declarationPattern = new MethodDeclarationPattern( enclosingTypes, selectorChars, matchRule, toolkit); } if (findReferences) { referencesPattern = new MethodPattern(findDeclarations, findReferences, selectorChars, declaringTypeQualification, declaringTypeSimpleName, declaringTypeSignature, null, matchRule, toolkit); } if (findDeclarations) { if (findReferences) { return new OrPattern(declarationPattern, referencesPattern); } return declarationPattern; } return referencesPattern; } return null; } /** * 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); // return null; } private static SearchPattern createScriptFolderPattern( String patternString, int limitTo, int matchRule) { // switch (limitTo) { // case IDLTKSearchConstants.DECLARATIONS : // return new PackageDeclarationPattern(patternString.toCharArray(), // matchRule); // case IDLTKSearchConstants.REFERENCES : // return new PackageReferencePattern(patternString.toCharArray(), // matchRule); // case IDLTKSearchConstants.ALL_OCCURRENCES : // return new OrPattern( // new PackageDeclarationPattern(patternString.toCharArray(), // matchRule), // new PackageReferencePattern(patternString.toCharArray(), matchRule) // ); // } return null; } /** * Returns a search pattern based on a given string pattern. The string * patterns support '*' wild-cards. The remaining parameters are used to * narrow down the type of expected results. * * <br> * Examples: * <ul> * <li>search for case insensitive references to <code>Object</code>: * <code>createSearchPattern("Object", TYPE, REFERENCES, false);</code></li> * <li>search for case sensitive references to exact <code>Object()</code> * constructor: * <code>createSearchPattern("java.lang.Object()", CONSTRUCTOR, REFERENCES, true);</code> * </li> * <li>search for implementers of <code>java.lang.Runnable</code>: * <code>createSearchPattern("java.lang.Runnable", TYPE, IMPLEMENTORS, true);</code> * </li> * </ul> * * @param stringPattern * the given pattern * @param searchFor * determines the nature of the searched elements * <ul> * <li>{@link IDLTKSearchConstants#CLASS}: only look for classes</li> * <li>{@link IDLTKSearchConstants#INTERFACE}: only look for * interfaces</li> * <li>{@link IDLTKSearchConstants#ENUM}: only look for * enumeration</li> * <li>{@link IDLTKSearchConstants#ANNOTATION_TYPE}: only look * for annotation type</li> * <li>{@link IDLTKSearchConstants#CLASS_AND_ENUM}: only look for * classes and enumerations</li> * <li>{@link IDLTKSearchConstants#CLASS_AND_INTERFACE}: only * look for classes and interfaces</li> * <li>{@link IDLTKSearchConstants#TYPE}: look for all types (ie. * classes, interfaces, enum and annotation types)</li> * <li>{@link IDLTKSearchConstants#FIELD}: look for fields</li> * <li>{@link IDLTKSearchConstants#METHOD}: look for methods</li> * <li>{@link IDLTKSearchConstants#CONSTRUCTOR}: look for * constructors</li> * <li>{@link IDLTKSearchConstants#PACKAGE}: look for packages</li> * </ul> * @param limitTo * determines the nature of the expected matches * <ul> * <li>{@link IDLTKSearchConstants#DECLARATIONS}: will search * declarations matching with the corresponding element. In case * the element is a method, declarations of matching methods in * subtypes will also be found, allowing to find declarations of * abstract methods, etc.<br> * Note that additional flags * {@link IDLTKSearchConstants#IGNORE_DECLARING_TYPE} and * {@link IDLTKSearchConstants#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 IDLTKSearchConstants#REFERENCES}: will search * references to the given element.</li> * <li>{@link IDLTKSearchConstants#ALL_OCCURRENCES}: will search * for either declarations or references as specified above.</li> * <li>{@link IDLTKSearchConstants#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 IDLTKSearchConstants#CLASS } or * {@link IDLTKSearchConstants#INTERFACE} is respectively used * instead of {@link IDLTKSearchConstants#TYPE}.</li> * </ul> * @param matchRule * one of {@link #R_EXACT_MATCH}, {@link #R_PREFIX_MATCH}, * {@link #R_PATTERN_MATCH}, {@link #R_REGEXP_MATCH}, * {@link #R_CAMELCASE_MATCH} combined with one of following * values: {@link #R_CASE_SENSITIVE}, {@link #R_ERASURE_MATCH} or * {@link #R_EQUIVALENT_MATCH}. e.g. {@link #R_EXACT_MATCH} | * {@link #R_CASE_SENSITIVE} if an exact and case sensitive match * is requested, {@link #R_PREFIX_MATCH} if a prefix non case * sensitive match is requested or {@link #R_EXACT_MATCH} | * {@link #R_ERASURE_MATCH} if a non case sensitive and erasure * match is requested.<br> * Note that {@link #R_ERASURE_MATCH} or * {@link #R_EQUIVALENT_MATCH} have no effect on non-generic * types/methods search.<br> * Note also that default behavior for generic types/methods * search is to find exact matches. * @return a search pattern on the given string pattern, or * <code>null</code> if the string pattern is ill-formed */ public static SearchPattern createPattern(String stringPattern, int searchFor, int limitTo, int matchRule, IDLTKLanguageToolkit toolkit) { if (stringPattern == null || stringPattern.length() == 0) return null; if ((matchRule = validateMatchRule(stringPattern, matchRule)) == -1) { return null; } // Ignore additional nature flags limitTo &= ~(IDLTKSearchConstants.IGNORE_DECLARING_TYPE + IDLTKSearchConstants.IGNORE_RETURN_TYPE); switch (searchFor) { case IDLTKSearchConstants.ANNOTATION_TYPE: return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.ANNOTATION_TYPE_SUFFIX, toolkit); case IDLTKSearchConstants.TYPE: return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.TYPE_SUFFIX, toolkit); case IDLTKSearchConstants.METHOD: return createMethodOrConstructorPattern(stringPattern, limitTo, matchRule, false/* * not a constructor */, toolkit); // case IDLTKSearchConstants.CONSTRUCTOR: // return createMethodOrConstructorPattern(stringPattern, limitTo, // matchRule, true/* constructor */); case IDLTKSearchConstants.FIELD: return createFieldPattern(stringPattern, limitTo, matchRule, toolkit); // case IDLTKSearchConstants.PACKAGE: // return createScriptFolderPattern(stringPattern, limitTo, // matchRule); } return null; } /** * Returns a search pattern based on a given Script element. The pattern is * used to trigger the appropriate search. <br> * Note that for generic searches, the returned pattern consider * {@link #R_ERASURE_MATCH} matches. If other kind of generic matches (ie. * {@link #R_EXACT_MATCH} or {@link #R_EQUIVALENT_MATCH}) are expected, * {@link #createPattern(IModelElement, 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 Script element the search pattern is based on * @param limitTo * determines the nature of the expected matches * <ul> * switch (limitTo) { // case IDLTKSearchConstants.DECLARATIONS : * // return new * PackageDeclarationPattern(patternString.toCharArray(), * matchRule); // case IDLTKSearchConstants.REFERENCES : // * return new * PackageReferencePattern(patternString.toCharArray(), * matchRule); // case IDLTKSearchConstants.ALL_OCCURRENCES : // * return new OrPattern( // new * PackageDeclarationPattern(patternString.toCharArray(), * matchRule), // new * PackageReferencePattern(patternString.toCharArray(), * matchRule) // ); // } * <li>{@link IDLTKSearchConstants#DECLARATIONS}: will search * declarations matching with the corresponding element. In case * the element is a method, declarations of matching methods in * subtypes will also be found, allowing to find declarations of * abstract methods, etc. Some additional flags may be specified * while searching declaration: * <ul> * <li>{@link IDLTKSearchConstants#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 IDLTKSearchConstants#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 IDLTKSearchConstants#REFERENCES}: will search * references to the given element.</li> * <li>{@link IDLTKSearchConstants#ALL_OCCURRENCES}: will search * for either declarations or references as specified above.</li> * <li>{@link IDLTKSearchConstants#IMPLEMENTORS}: for types, will * find all types which directly implement/extend a given * interface.</li> * </ul> * @return a search pattern for a Script element or <code>null</code> if the * given element is ill-formed */ public static SearchPattern createPattern(IModelElement element, int limitTo) { return createPattern(element, limitTo, R_EXACT_MATCH | R_CASE_SENSITIVE | R_ERASURE_MATCH, DLTKLanguageManager.getLanguageToolkit(element)); } /** * Returns a search pattern based on a given Script element. The pattern is * used to trigger the appropriate search, and can be parameterized as * follows: * * @param element * the Script element the search pattern is based on * @param limitTo * determines the nature of the expected matches * <ul> * <li>{@link IDLTKSearchConstants#DECLARATIONS}: will search * declarations matching with the corresponding element. In case * the element is a method, declarations of matching methods in * subtypes will also be found, allowing to find declarations of * abstract methods, etc. Some additional flags may be specified * while searching declaration: * <ul> * <li>{@link IDLTKSearchConstants#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 IDLTKSearchConstants#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 IDLTKSearchConstants#REFERENCES}: will search * references to the given element.</li> * <li>{@link IDLTKSearchConstants#ALL_OCCURRENCES}: will search * for either declarations or references as specified above.</li> * <li>{@link IDLTKSearchConstants#IMPLEMENTORS}: for types, will * find all types which directly implement/extend a given * interface.</li> * </ul> * @param matchRule * one of {@link #R_EXACT_MATCH}, {@link #R_PREFIX_MATCH}, * {@link #R_PATTERN_MATCH}, {@link #R_REGEXP_MATCH}, * {@link #R_CAMELCASE_MATCH} combined with one of following * values: {@link #R_CASE_SENSITIVE}, {@link #R_ERASURE_MATCH} or * {@link #R_EQUIVALENT_MATCH}. e.g. {@link #R_EXACT_MATCH} | * {@link #R_CASE_SENSITIVE} if an exact and case sensitive match * is requested, {@link #R_PREFIX_MATCH} if a prefix non case * sensitive match is requested or {@link #R_EXACT_MATCH} | * {@link #R_ERASURE_MATCH} if a non case sensitive and erasure * match is requested.<br> * Note that {@link #R_ERASURE_MATCH} or * {@link #R_EQUIVALENT_MATCH} have no effect on non-generic * types or methods search.<br> * Note also that default behavior for generic types or methods * is to find exact matches. * @return a search pattern for a Script element or <code>null</code> if the * given element is ill-formed * */ public static SearchPattern createPattern(IModelElement element, int limitTo, int matchRule, IDLTKLanguageToolkit toolkit) { SearchPattern searchPattern = null; boolean ignoreDeclaringType = false; // boolean ignoreReturnType = false; int maskedLimitTo = limitTo & ~(IDLTKSearchConstants.IGNORE_DECLARING_TYPE + IDLTKSearchConstants.IGNORE_RETURN_TYPE); if (maskedLimitTo == IDLTKSearchConstants.DECLARATIONS || maskedLimitTo == IDLTKSearchConstants.ALL_OCCURRENCES) { ignoreDeclaringType = (limitTo & IDLTKSearchConstants.IGNORE_DECLARING_TYPE) != 0; // ignoreReturnType = (limitTo & // IDLTKSearchConstants.IGNORE_RETURN_TYPE) != 0; } char[] declaringSimpleName = null; char[] declaringQualification = null; char[][] enclosingNames = null; switch (element.getElementType()) { case IModelElement.METHOD: IMethod method = (IMethod) element; boolean isConstructor; try { isConstructor = method.isConstructor(); } catch (ModelException e) { return null; } IType declaringClass = method.getDeclaringType(); if (ignoreDeclaringType) { if (isConstructor) { declaringSimpleName = declaringClass.getElementName() .toCharArray(); enclosingNames = new char[][] { declaringSimpleName }; } } else { if (declaringClass != null) { declaringSimpleName = declaringClass.getElementName() .toCharArray(); // IModelElement parent = declaringClass.getParent(); // if (parent.getElementType() == IModelElement.TYPE) { // declaringQualification = // ((IType)parent).getTypeQualifiedName().toCharArray(); // } enclosingNames = enclosingTypeNames(element); if (enclosingNames.length > 0) { declaringSimpleName = CharOperation.concat( declaringQualification, CharOperation.concatWith(enclosingNames, '$'), '$'); } } } char[] selector = method.getElementName().toCharArray(); if (selector.length == 0) { // TODO introduce pattern for anonymous functions? (similar to // local variables) return null; } // Create method/constructor pattern boolean findMethodDeclarations = true; boolean findMethodReferences = true; switch (maskedLimitTo) { case IDLTKSearchConstants.DECLARATIONS: findMethodReferences = false; break; case IDLTKSearchConstants.REFERENCES: findMethodDeclarations = false; break; case IDLTKSearchConstants.ALL_OCCURRENCES: break; } MethodDeclarationPattern declarationPattern = null; MethodPattern referencesPattern = null; if (findMethodDeclarations) { declarationPattern = new MethodDeclarationPattern( enclosingNames, selector, matchRule, toolkit); } if (findMethodReferences) { referencesPattern = new MethodPattern(findMethodDeclarations, findMethodReferences, selector, declaringQualification, declaringSimpleName, method, matchRule, toolkit); } if (findMethodDeclarations) { if (findMethodReferences) { searchPattern = new OrPattern(declarationPattern, referencesPattern); } else { searchPattern = declarationPattern; } } else { searchPattern = referencesPattern; } break; case IModelElement.TYPE: IType type = (IType) element; char[] packageName = null; char[][] superTypes = null; try { superTypes = CharOperation.stringArrayToCharCharArray(type .getSuperClasses()); packageName = createPackagePattern(type.getNamespace()); } catch (ModelException e) { return null; } searchPattern = createTypePattern(type.getElementName() .toCharArray(), packageName, ignoreDeclaringType ? null : enclosingTypeNames(type), superTypes, null, type, maskedLimitTo, matchRule); break; case IModelElement.SCRIPT_FOLDER: searchPattern = createScriptFolderPattern(element.getElementName(), maskedLimitTo, matchRule); break; case IModelElement.FIELD: searchPattern = createFieldPattern(element.getElementName(), maskedLimitTo, matchRule, toolkit); break; case IModelElement.LOCAL_VARIABLE: searchPattern = new LocalVariablePattern((ILocalVariable) element, maskedLimitTo, matchRule, toolkit); break; } if (searchPattern != null) MatchLocator.setFocus(searchPattern, element); return searchPattern; } static char[] createPackagePattern(INamespace namespace) { if (namespace != null) { return CharOperation.concatWith(namespace.getStrings(), TYPE_SEPARATOR); } else { return null; } } private static SearchPattern createTypePattern(char[] simpleName, char[] packageName, char[][] enclosingTypeNames, char[][] superTypes, String typeSignature, IType type, int limitTo, int matchRule) { IDLTKLanguageToolkit toolkit = DLTKLanguageManager .getLanguageToolkit(type); switch (limitTo) { case IDLTKSearchConstants.DECLARATIONS: return new TypeDeclarationPattern(packageName, enclosingTypeNames, superTypes, simpleName, IIndexConstants.TYPE_SUFFIX, matchRule, toolkit); case IDLTKSearchConstants.REFERENCES: if (type != null) { return new TypeReferencePattern(CharOperation.concatWith( packageName, enclosingTypeNames, '$'), simpleName, type, matchRule, toolkit); } return new TypeReferencePattern(CharOperation.concatWith( packageName, enclosingTypeNames, '$'), simpleName, matchRule, toolkit); // case IDLTKSearchConstants.IMPLEMENTORS : // return new SuperTypeReferencePattern( // CharOperation.concatWith(packageName, enclosingTypeNames, // '.'), // simpleName, // SuperTypeReferencePattern.ONLY_SUPER_INTERFACES, // matchRule); case IDLTKSearchConstants.ALL_OCCURRENCES: return new OrPattern(new TypeDeclarationPattern(packageName, enclosingTypeNames, superTypes, simpleName, IIndexConstants.TYPE_SUFFIX, matchRule, toolkit), (type != null) ? new TypeReferencePattern(CharOperation .concatWith(packageName, enclosingTypeNames, '$'), simpleName, type, matchRule, toolkit) : new TypeReferencePattern(CharOperation .concatWith(packageName, enclosingTypeNames, '$'), simpleName, matchRule, toolkit)); } return null; } /** * Type pattern are formed by [qualification '.']type [typeArguments]. e.g. * java.lang.Object Runnable List<String> * * * parameterized types. and look as follow: '<' { [ '?' * {'extends'|'super'} ] type ( ',' [ '?' {'extends'|'super'} ] type )* | * '?' } '>' Please note that: - '*' is not valid inside type arguments * definition <> - '?' is treated as a wildcard when it is inside * <> (ie. it must be put on first position of the type argument) */ private static SearchPattern createTypePattern(String patternString, int limitTo, int matchRule, char indexSuffix, IDLTKLanguageToolkit toolkit) { String type = patternString; if (type == null) return null; char[] qualificationChars; char[] typeChars; { ITypePattern typePattern = DLTKLanguageManager .getSearchPatternProcessor(toolkit, true).parseType( patternString); qualificationChars = typePattern.qualification(); typeChars = typePattern.simpleName(); } if (typeChars.length == 1 && typeChars[0] == '*') { typeChars = null; } switch (limitTo) { case IDLTKSearchConstants.DECLARATIONS: // cannot search for explicit // member types return new QualifiedTypeDeclarationPattern(qualificationChars, typeChars, indexSuffix, matchRule, toolkit); case IDLTKSearchConstants.REFERENCES: return new TypeReferencePattern(qualificationChars, typeChars, matchRule, toolkit); // case IDLTKSearchConstants.IMPLEMENTORS : // return new SuperTypeReferencePattern(qualificationChars, // typeChars, SuperTypeReferencePattern.ONLY_SUPER_INTERFACES, // indexSuffix, matchRule); case IDLTKSearchConstants.ALL_OCCURRENCES: return new OrPattern(new QualifiedTypeDeclarationPattern( qualificationChars, typeChars, indexSuffix, matchRule, toolkit),// cannot // search // for // explicit // member // types new TypeReferencePattern(qualificationChars, typeChars, matchRule, toolkit)); } return null; } /** * Returns the enclosing type names of the given element. */ private static char[][] enclosingTypeNames(IModelElement element) { IModelElement parent = element.getParent(); switch (parent.getElementType()) { case IModelElement.SOURCE_MODULE: return CharOperation.NO_CHAR_CHAR; case IModelElement.FIELD: case IModelElement.METHOD: IType declaringClass = ((IMember) parent).getDeclaringType(); if (declaringClass == null) return null; return CharOperation.arrayConcat( enclosingTypeNames(declaringClass), new char[][] { declaringClass.getElementName().toCharArray(), IIndexConstants.ONE_STAR }); case IModelElement.TYPE: return CharOperation.arrayConcat( enclosingTypeNames((IType) parent), parent.getElementName() .toCharArray()); default: return null; } } /** * Decode the given index key in this pattern. The decoded index key is used * by {@link #matchesDecodedKey(SearchPattern)} to find out if the * corresponding index entry should be considered. * <p> * This method should be re-implemented in subclasses that need to decode an * index key. * </p> * * @param key * the given index key */ public void decodeIndexKey(char[] key) { // called from findIndexMatches(), override as necessary } /** * Returns a blank pattern that can be used as a record to decode an index * key. * <p> * Implementors of this method should return a new search pattern that is * going to be used to decode index keys. * </p> * * @return a new blank pattern * @see #decodeIndexKey(char[]) */ public abstract SearchPattern getBlankPattern(); /** * Returns a key to find in relevant index categories, if null then all * index entries are matched. The key will be matched according to some * match rule. These potential matches will be further narrowed by the match * locator, but precise match locating can be expensive, and index query * should be as accurate as possible so as to eliminate obvious false hits. * <p> * This method should be re-implemented in subclasses that need to narrow * down the index query. * </p> * * @return an index key from this pattern, or <code>null</code> if all index * entries are matched. */ public char[] getIndexKey() { return null; // called from queryIn(), override as necessary } /** * Returns an array of index categories to consider for this index query. * These potential matches will be further narrowed by the match locator, * but precise match locating can be expensive, and index query should be as * accurate as possible so as to eliminate obvious false hits. * <p> * This method should be re-implemented in subclasses that need to narrow * down the index query. * </p> * * @return an array of index categories */ public char[][] getIndexCategories() { return CharOperation.NO_CHAR_CHAR; // called from queryIn(), override // as necessary } /** * Returns the rule to apply for matching index keys. Can be exact match, * prefix match, pattern match or regexp match. Rule can also be combined * with a case sensitivity flag. * * @return one of R_EXACT_MATCH, R_PREFIX_MATCH, R_PATTERN_MATCH, * R_REGEXP_MATCH combined with R_CASE_SENSITIVE, e.g. R_EXACT_MATCH * | R_CASE_SENSITIVE if an exact and case sensitive match is * requested, or R_PREFIX_MATCH if a prefix non case sensitive match * is requested. [TODO (frederic) I hope R_ERASURE_MATCH doesn't * need to be on this list. Because it would be a breaking API * change.] */ public final int getMatchRule() { return this.matchRule; } /** * Returns whether this pattern matches the given pattern (representing a * decoded index key). * <p> * This method should be re-implemented in subclasses that need to narrow * down the index query. * </p> * * @param decodedPattern * a pattern representing a decoded index key * @return whether this pattern matches the given pattern */ public boolean matchesDecodedKey(SearchPattern decodedPattern) { return true; // called from findIndexMatches(), override as necessary // if index key is encoded } /** * Returns whether the given name matches the given pattern. * <p> * This method should be re-implemented in subclasses that need to define * how a name matches a pattern. * </p> * * @param pattern * the given pattern, or <code>null</code> to represent "*" * @param name * the given name * @return whether the given name matches the given pattern */ public boolean matchesName(char[] pattern, char[] name) { if (pattern == null) return true; // null is as if it was "*" if (name != null) { boolean isCaseSensitive = (this.matchRule & R_CASE_SENSITIVE) != 0; boolean isCamelCase = (this.matchRule & R_CAMELCASE_MATCH) != 0; int matchMode = this.matchRule & MODE_MASK; boolean emptyPattern = pattern.length == 0; if (matchMode == R_PREFIX_MATCH && emptyPattern) return true; boolean sameLength = pattern.length == name.length; boolean canBePrefix = name.length >= pattern.length; boolean matchFirstChar = !isCaseSensitive || emptyPattern || (name.length > 0 && pattern[0] == name[0]); if (isCamelCase && matchFirstChar && CharOperation.camelCaseMatch(pattern, name)) { return true; } switch (matchMode) { case R_EXACT_MATCH: case R_FULL_MATCH: if (!isCamelCase) { if (sameLength && matchFirstChar) { return CharOperation.equals(pattern, name, isCaseSensitive); } break; } // fall through next case to match as prefix if camel case // failed case R_PREFIX_MATCH: if (canBePrefix && matchFirstChar) { return CharOperation.prefixEquals(pattern, name, isCaseSensitive); } break; case R_PATTERN_MATCH: if (!isCaseSensitive) pattern = CharOperation.toLowerCase(pattern); return CharOperation.match(pattern, name, isCaseSensitive); case R_REGEXP_MATCH: if (regexpCompiledPattern == null) { regexpCompiledPattern = Pattern.compile( new String(pattern), isCaseSensitive ? 0 : Pattern.CASE_INSENSITIVE); } return regexpCompiledPattern.matcher(new String(name)) .matches(); } } return false; } /** * Validate compatibility between given string pattern and match rule. <br> * Optimized (ie. returned match rule is modified) combinations are: * <ul> * <li>{@link #R_PATTERN_MATCH} without any '*' or '?' in string pattern: * pattern match bit is unset,</li> * <li>{@link #R_PATTERN_MATCH} and {@link #R_PREFIX_MATCH} bits * simultaneously set: prefix match bit is unset,</li> * <li>{@link #R_PATTERN_MATCH} and {@link #R_CAMELCASE_MATCH} bits * simultaneously set: camel case match bit is unset,</li> * <li>{@link #R_CAMELCASE_MATCH} with invalid combination of uppercase and * lowercase characters: camel case match bit is unset and replaced with * prefix match pattern,</li> * <li>{@link #R_CAMELCASE_MATCH} combined with {@link #R_PREFIX_MATCH} and * {@link #R_CASE_SENSITIVE} bits is reduced to only * {@link #R_CAMELCASE_MATCH} as Camel Case search is already prefix and * case sensitive,</li> * </ul> * <br> * Rejected (ie. returned match rule -1) combinations are: * <ul> * <li>{@link #R_REGEXP_MATCH} with any other match mode bit set,</li> * <li>{@link #R_REGEXP_MATCH} with wrong regular expression pattern given,</li> * </ul> * * @param stringPattern * The string pattern * @param matchRule * The match rule * @return Optimized valid match rule or -1 if an incompatibility was * detected. * */ public static int validateMatchRule(String stringPattern, int matchRule) { // Verify Regexp match rule if ((matchRule & R_REGEXP_MATCH) != 0) { if ((matchRule & R_PATTERN_MATCH) != 0 || (matchRule & R_PREFIX_MATCH) != 0 || (matchRule & R_CAMELCASE_MATCH) != 0) { return -1; } try { Pattern.compile(stringPattern); } catch (PatternSyntaxException e) { return -1; } } // Verify Pattern match rule int starIndex = stringPattern.indexOf('*'); int questionIndex = stringPattern.indexOf('?'); if (starIndex < 0 && questionIndex < 0) { // reset pattern match bit if any matchRule &= ~R_PATTERN_MATCH; } else { // force Pattern rule matchRule |= R_PATTERN_MATCH; } if ((matchRule & R_PATTERN_MATCH) != 0) { // remove Camel Case and Prefix match bits if any matchRule &= ~R_CAMELCASE_MATCH; matchRule &= ~R_PREFIX_MATCH; } // Verify Camel Case match rule if ((matchRule & R_CAMELCASE_MATCH) != 0) { // Verify sting pattern validity int length = stringPattern.length(); boolean validCamelCase = true; boolean uppercase = false; for (int i = 0; i < length && validCamelCase; i++) { char ch = stringPattern.charAt(i); validCamelCase = ScannerHelper.isScriptIdentifierStart(ch); // at least one uppercase character is need in CamelCase pattern // (see bug // https://bugs.eclipse.org/bugs/show_bug.cgi?id=136313) if (!uppercase) uppercase = ScannerHelper.isUpperCase(ch); } validCamelCase = validCamelCase && uppercase; // Verify bits compatibility if (validCamelCase) { if ((matchRule & R_PREFIX_MATCH) != 0) { if ((matchRule & R_CASE_SENSITIVE) != 0) { // This is equivalent to Camel Case match rule matchRule &= ~R_PREFIX_MATCH; matchRule &= ~R_CASE_SENSITIVE; } } } else { matchRule &= ~R_CAMELCASE_MATCH; if ((matchRule & R_PREFIX_MATCH) == 0) { matchRule |= R_PREFIX_MATCH; matchRule |= R_CASE_SENSITIVE; } } } return matchRule; } /** * @see java.lang.Object#toString() */ public String toString() { return "SearchPattern"; //$NON-NLS-1$ } }