/******************************************************************************* * Copyright (c) 2000, 2016 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: * xored software, Inc. - initial API and Implementation * xored software, Inc. - Search All occurences bugfix, * hilight only class name when class is in search results ( Alex Panchenko <alex@xored.com>) *******************************************************************************/ package org.eclipse.dltk.core.search.matching; import java.text.Annotation; import java.util.regex.Pattern; import org.eclipse.core.runtime.CoreException; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.ast.declarations.FieldDeclaration; import org.eclipse.dltk.ast.declarations.MethodDeclaration; import org.eclipse.dltk.ast.declarations.TypeDeclaration; import org.eclipse.dltk.ast.expressions.CallExpression; import org.eclipse.dltk.ast.expressions.Expression; import org.eclipse.dltk.ast.references.Reference; import org.eclipse.dltk.ast.references.TypeReference; import org.eclipse.dltk.compiler.CharOperation; import org.eclipse.dltk.compiler.env.lookup.Scope; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.IDLTKLanguageToolkit; import org.eclipse.dltk.core.IModelElement; import org.eclipse.dltk.core.search.SearchMatch; import org.eclipse.dltk.core.search.SearchPattern; import org.eclipse.dltk.core.search.indexing.IIndexConstants; import org.eclipse.dltk.internal.core.search.matching.DLTKSearchPattern; import org.eclipse.dltk.internal.core.search.matching.FieldLocator; 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.MatchingNodeSet; import org.eclipse.dltk.internal.core.search.matching.MethodDeclarationLocator; import org.eclipse.dltk.internal.core.search.matching.MethodDeclarationPattern; import org.eclipse.dltk.internal.core.search.matching.MethodLocator; import org.eclipse.dltk.internal.core.search.matching.MethodPattern; import org.eclipse.dltk.internal.core.search.matching.OrLocator; import org.eclipse.dltk.internal.core.search.matching.OrPattern; import org.eclipse.dltk.internal.core.search.matching.TypeDeclarationLocator; import org.eclipse.dltk.internal.core.search.matching.TypeDeclarationPattern; import org.eclipse.dltk.internal.core.search.matching.TypeReferenceLocator; import org.eclipse.dltk.internal.core.search.matching.TypeReferencePattern; public abstract class PatternLocator implements IIndexConstants { // store pattern info protected int matchMode; protected boolean isCaseSensitive; protected boolean isCamelCase; protected boolean isEquivalentMatch; protected boolean isErasureMatch; protected Pattern compiledPattern; // match to report SearchMatch match = null; /* match levels */ public static final int IMPOSSIBLE_MATCH = 0; public static final int INACCURATE_MATCH = 1; public static final int POSSIBLE_MATCH = 2; public static final int ACCURATE_MATCH = 3; public static final int ERASURE_MATCH = 4; // Possible rule match flavors // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=79866 protected static final int POSSIBLE_FULL_MATCH = POSSIBLE_MATCH | (SearchPattern.R_FULL_MATCH << 16); protected static final int POSSIBLE_PREFIX_MATCH = POSSIBLE_MATCH | (SearchPattern.R_PREFIX_MATCH << 16); protected static final int POSSIBLE_PATTERN_MATCH = POSSIBLE_MATCH | (SearchPattern.R_PATTERN_MATCH << 16); protected static final int POSSIBLE_REGEXP_MATCH = POSSIBLE_MATCH | (SearchPattern.R_REGEXP_MATCH << 16); protected static final int POSSIBLE_CAMELCASE_MATCH = POSSIBLE_MATCH | (SearchPattern.R_CAMELCASE_MATCH << 16); public static final int NODE_SET_MASK = 0xFF; protected static final int POSSIBLE_MATCH_MASK = ~NODE_SET_MASK; /* match container */ public static final int COMPILATION_UNIT_CONTAINER = 1; public static final int CLASS_CONTAINER = 2; public static final int METHOD_CONTAINER = 4; public static final int FIELD_CONTAINER = 8; public static final int ALL_CONTAINER = COMPILATION_UNIT_CONTAINER | CLASS_CONTAINER | METHOD_CONTAINER | FIELD_CONTAINER; /* match rule */ public static final int RAW_MASK = SearchPattern.R_EQUIVALENT_MATCH | SearchPattern.R_ERASURE_MATCH; public static final int RULE_MASK = RAW_MASK; // no other values for the // while... public static PatternLocator patternLocator(SearchPattern pattern, IDLTKLanguageToolkit toolkit) { switch (((InternalSearchPattern) pattern).kind) { // case IIndexConstants.PKG_REF_PATTERN: // return new PackageReferenceLocator((PackageReferencePattern) // pattern); // case IIndexConstants.PKG_DECL_PATTERN: // return new PackageDeclarationLocator((PackageDeclarationPattern) // pattern); case IIndexConstants.TYPE_REF_PATTERN: return new TypeReferenceLocator((TypeReferencePattern) pattern); case IIndexConstants.TYPE_DECL_PATTERN: return new TypeDeclarationLocator((TypeDeclarationPattern) pattern); case IIndexConstants.METHOD_DECL_PATTERN: return new MethodDeclarationLocator( (MethodDeclarationPattern) pattern); // case IIndexConstants.SUPER_REF_PATTERN: // return new // SuperTypeReferenceLocator((SuperTypeReferencePattern) // pattern); // case IIndexConstants.CONSTRUCTOR_PATTERN: // return new ConstructorLocator((ConstructorPattern) pattern); case IIndexConstants.FIELD_PATTERN: return new FieldLocator((FieldPattern) pattern); case IIndexConstants.METHOD_PATTERN: return new MethodLocator((MethodPattern) pattern); case IIndexConstants.OR_PATTERN: return new OrLocator((OrPattern) pattern); } return null; } public static char[] qualifiedPattern(char[] simpleNamePattern, char[] qualificationPattern) { // NOTE: if case insensitive search then simpleNamePattern & // qualificationPattern are assumed to be lowercase if (simpleNamePattern == null) { if (qualificationPattern == null) return null; return CharOperation.concat(qualificationPattern, ONE_STAR, '.'); } else { return qualificationPattern == null ? CharOperation.concat( ONE_STAR, simpleNamePattern) : CharOperation.concat( qualificationPattern, simpleNamePattern, '.'); } } public PatternLocator(SearchPattern pattern) { int matchRule = pattern.getMatchRule(); this.isCaseSensitive = (matchRule & SearchPattern.R_CASE_SENSITIVE) != 0; this.isCamelCase = (matchRule & SearchPattern.R_CAMELCASE_MATCH) != 0; this.isErasureMatch = (matchRule & SearchPattern.R_ERASURE_MATCH) != 0; this.isEquivalentMatch = (matchRule & SearchPattern.R_EQUIVALENT_MATCH) != 0; this.matchMode = matchRule & DLTKSearchPattern.MATCH_MODE_MASK; } /* * Clear caches */ protected void clear() { // nothing to clear by default } /* * (non-Javadoc) Modify PatternLocator.qualifiedPattern behavior: do not add * star before simple name pattern when qualification pattern is null. This * avoid to match p.X when pattern is only X... */ protected char[] getQualifiedPattern(char[] simpleNamePattern, char[] qualificationPattern) { // NOTE: if case insensitive search then simpleNamePattern & // qualificationPattern are assumed to be lowercase if (simpleNamePattern == null) { if (qualificationPattern == null) return null; return CharOperation.concat(qualificationPattern, ONE_STAR, '.'); } else if (qualificationPattern == null) { return simpleNamePattern; } else { return CharOperation.concat(qualificationPattern, simpleNamePattern, '.'); } } /** * Initializes this search pattern so that polymorphic search can be * performed. */ public void initializePolymorphicSearch(MatchLocator locator) { // default is to do nothing } public int match(Annotation node, MatchingNodeSet nodeSet) { // each subtype should override if needed return IMPOSSIBLE_MATCH; } /** * Check if the given ast node syntactically matches this pattern. If it * does, add it to the match set. Returns the match level. */ public int match(ASTNode node, MatchingNodeSet nodeSet) { // needed for // some generic // nodes // each subtype should override if needed return IMPOSSIBLE_MATCH; } public int match(CallExpression node, MatchingNodeSet nodeSet) { // needed // for // each subtype should override if needed return IMPOSSIBLE_MATCH; } public int match(Expression node, MatchingNodeSet nodeSet) { // each subtype should override if needed return IMPOSSIBLE_MATCH; } public int match(MethodDeclaration node, MatchingNodeSet nodeSet) { // each subtype should override if needed return IMPOSSIBLE_MATCH; } public int match(Reference node, MatchingNodeSet nodeSet) { // each subtype should override if needed return IMPOSSIBLE_MATCH; } public int match(TypeReference node, MatchingNodeSet nodeSet) { // each subtype should override if needed return IMPOSSIBLE_MATCH; } public int match(FieldDeclaration node, MatchingNodeSet nodeSet) { // each subtype should override if needed return IMPOSSIBLE_MATCH; } public int match(TypeDeclaration node, MatchingNodeSet nodeSet) { // each subtype should override if needed return IMPOSSIBLE_MATCH; } /** * Returns the type(s) of container for this pattern. It is a bit * combination of types, denoting compilation unit, class declarations, * field declarations or method declarations. */ public int matchContainer() { // override if the pattern can be more specific return ALL_CONTAINER; } /** * Returns whether the given name matches the given pattern. */ protected boolean matchesName(char[] pattern, char[] name) { if (pattern == null) return true; // null is as if it was "*" if (name == null) return false; // cannot match null name return matchNameValue(pattern, name) != IMPOSSIBLE_MATCH; } /** * Return how the given name matches the given pattern. * * @see "https://bugs.eclipse.org/bugs/show_bug.cgi?id=79866" * * @param pattern * @param name * @return Possible values are: * <ul> * <li>{@link #POSSIBLE_FULL_MATCH}: Given name is equals to pattern * </li> * <li>{@link #POSSIBLE_PREFIX_MATCH}: Given name prefix equals to * pattern</li> * <li>{@link #POSSIBLE_CAMELCASE_MATCH}: Given name matches pattern * as Camel Case</li> * <li>{@link #POSSIBLE_PATTERN_MATCH}: Given name matches pattern * as Pattern (ie. using '*' and '?' characters)</li> * </ul> */ protected int matchNameValue(char[] pattern, char[] name) { if (pattern == null) return ACCURATE_MATCH; // null is as if it was "*" if (name == null) return IMPOSSIBLE_MATCH; // cannot match null name if (name.length == 0) { // empty name if (pattern.length == 0) { // can only matches empty pattern return ACCURATE_MATCH; } return IMPOSSIBLE_MATCH; } else if (pattern.length == 0) { return IMPOSSIBLE_MATCH; // need to have both name and pattern // length==0 to be accurate } boolean matchFirstChar = !this.isCaseSensitive || pattern[0] == name[0]; boolean sameLength = pattern.length == name.length; boolean canBePrefix = name.length >= pattern.length; if (this.isCamelCase && matchFirstChar && CharOperation.camelCaseMatch(pattern, name)) { return POSSIBLE_CAMELCASE_MATCH; } switch (this.matchMode) { case SearchPattern.R_EXACT_MATCH: if (!this.isCamelCase) { if (sameLength && matchFirstChar && CharOperation.equals(pattern, name, this.isCaseSensitive)) { return POSSIBLE_FULL_MATCH; } break; } // fall through next case to match as prefix if camel case // failed case SearchPattern.R_PREFIX_MATCH: if (canBePrefix && matchFirstChar && CharOperation.prefixEquals(pattern, name, this.isCaseSensitive)) { return POSSIBLE_PREFIX_MATCH; } break; case SearchPattern.R_PATTERN_MATCH: if (!this.isCaseSensitive) { pattern = CharOperation.toLowerCase(pattern); } if (CharOperation.match(pattern, name, this.isCaseSensitive)) { return POSSIBLE_MATCH; } break; case SearchPattern.R_REGEXP_MATCH: if (compiledPattern == null) { compiledPattern = Pattern.compile(new String(pattern), this.isCaseSensitive ? 0 : Pattern.CASE_INSENSITIVE); } if (compiledPattern.matcher(new String(name)).matches()) { return POSSIBLE_REGEXP_MATCH; } break; } return IMPOSSIBLE_MATCH; } /** * Reports the match of the given reference. */ protected void matchReportReference(ASTNode reference, IModelElement element, int accuracy, MatchLocator locator) throws CoreException { match = null; switch (referenceType()) { case IModelElement.TYPE: match = locator.newTypeReferenceMatch(element, accuracy, reference .matchStart(), reference.matchLength(), reference); break; case IModelElement.FIELD: match = locator.newFieldReferenceMatch(element, accuracy, reference .matchStart(), reference.matchLength(), reference); break; case IModelElement.METHOD: match = locator.newMethodReferenceMatch(element, accuracy, reference.matchStart(), reference.matchLength(), false, false, reference); break; } if (match != null) { locator.report(match); } } /** * Reports the match of the given reference. Also provide a local element to * eventually report in match. */ protected void matchReportReference(ASTNode reference, IModelElement element, IModelElement localElement, IModelElement[] otherElements, int accuracy, MatchLocator locator) throws CoreException { matchReportReference(reference, element, accuracy, locator); } /** * Reports the match of the given reference. Also provide a scope to look * for potential other elements. */ public void matchReportReference(ASTNode reference, IModelElement element, Scope scope, int accuracy, MatchLocator locator) throws CoreException { matchReportReference(reference, element, accuracy, locator); } /** * @deprecated */ @Deprecated public final SearchMatch newDeclarationMatch(ASTNode reference, IModelElement element, int accuracy, int length, MatchLocator locator) { return newDeclarationMatch(reference, element, accuracy, locator); } public SearchMatch newDeclarationMatch(ASTNode reference, IModelElement element, int accuracy, MatchLocator locator) { return locator.newDeclarationMatch(element, accuracy, reference .matchStart(), reference.matchLength()); } protected int referenceType() { return 0; // defaults to unknown (a generic JavaSearchMatch will be // created) } /** * Finds out whether the given ast node matches this search pattern. Returns * IMPOSSIBLE_MATCH if it doesn't. Returns INACCURATE_MATCH if it * potentially matches this search pattern (ie. it has already been resolved * but resolving failed.) Returns ACCURATE_MATCH if it matches exactly this * search pattern (ie. it doesn't need to be resolved or it has already been * resolved.) */ public int resolveLevel(ASTNode possibleMatchingNode) { // only called with nodes which were possible matches to the call to // matchLevel // need to do instance of checks to find out exact type of ASTNode return IMPOSSIBLE_MATCH; } /* * Update pattern locator match comparing type arguments with pattern ones. * Try to resolve pattern and look for compatibility with type arguments to * set match rule. */ protected void updateMatch(MatchLocator locator, char[][] patternArguments, boolean hasTypeParameters) { if (DLTKCore.DEBUG) { System.err.println("TODO: updateMatch"); //$NON-NLS-1$ } } /** * Returns whether the given type binding matches the given simple name * pattern and qualification pattern. Note that this method resolve to * accurate member or local types even if they are not fully qualified (ie. * X.Member instead of p.X.Member). Returns ACCURATE_MATCH if it does. * Returns INACCURATE_MATCH if resolve failed. Returns IMPOSSIBLE_MATCH if * it doesn't. */ protected int resolveLevelForType(char[] simpleNamePattern, char[] qualificationPattern) { return INACCURATE_MATCH; } /** * Returns whether the given type binding matches the given qualified * pattern. Returns ACCURATE_MATCH if it does. Returns INACCURATE_MATCH if * resolve failed. Returns IMPOSSIBLE_MATCH if it doesn't. */ protected int resolveLevelForType(char[] qualifiedPattern) { return IMPOSSIBLE_MATCH; } @Override public String toString() { return "SearchPattern"; //$NON-NLS-1$ } }