| /******************************************************************************* |
| * Copyright (c) 2004, 2009 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core.search.matching; |
| |
| import org.eclipse.jdt.core.BindingKey; |
| import org.eclipse.jdt.core.IJavaElement; |
| 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.search.IJavaSearchConstants; |
| import org.eclipse.jdt.core.search.SearchPattern; |
| import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants; |
| import org.eclipse.jdt.internal.core.util.Util; |
| |
| |
| public class JavaSearchPattern extends SearchPattern implements IIndexConstants { |
| |
| /* |
| * Whether this pattern is case sensitive. |
| */ |
| boolean isCaseSensitive; |
| /* |
| * Whether this pattern is camel case. |
| */ |
| boolean isCamelCase; |
| |
| /** |
| * One of following pattern value: |
| * <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> |
| */ |
| int matchMode; |
| |
| /** |
| * One of {@link #R_ERASURE_MATCH}, {@link #R_EQUIVALENT_MATCH}, {@link #R_FULL_MATCH}. |
| */ |
| int matchCompatibility; |
| |
| /** |
| * Fine grain limitation |
| */ |
| public int fineGrain = 0; |
| |
| /** |
| * Mask used on match rule for match mode. |
| */ |
| public static final int MATCH_MODE_MASK = R_EXACT_MATCH |
| | R_PREFIX_MATCH |
| | R_PATTERN_MATCH |
| | R_REGEXP_MATCH |
| | R_CAMELCASE_MATCH |
| | R_CAMELCASE_SAME_PART_COUNT_MATCH; |
| |
| /** |
| * Mask used on match rule for generic relevance. |
| */ |
| public static final int MATCH_COMPATIBILITY_MASK = R_ERASURE_MATCH | R_EQUIVALENT_MATCH | R_FULL_MATCH; |
| |
| // Signatures and arguments for parameterized types search |
| char[][] typeSignatures; |
| private char[][][] typeArguments; |
| private int flags = 0; |
| static final int HAS_TYPE_ARGUMENTS = 1; |
| |
| protected JavaSearchPattern(int patternKind, int matchRule) { |
| super(matchRule); |
| this.kind = patternKind; |
| // Use getMatchRule() instead of matchRule as super constructor may modify its value |
| // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=81377 |
| int rule = getMatchRule(); |
| this.isCaseSensitive = (rule & R_CASE_SENSITIVE) != 0; |
| this.isCamelCase = (rule & (R_CAMELCASE_MATCH | R_CAMELCASE_SAME_PART_COUNT_MATCH)) != 0; |
| this.matchCompatibility = rule & MATCH_COMPATIBILITY_MASK; |
| this.matchMode = rule & MATCH_MODE_MASK; |
| } |
| |
| /** |
| * @param fineGrain |
| */ |
| public static String getFineGrainFlagString(final int fineGrain) { |
| if (fineGrain == 0) { |
| return "none"; //$NON-NLS-1$ |
| } |
| StringBuffer buffer = new StringBuffer(); |
| for (int i=1; i<=32; i++) { |
| int bit = fineGrain & (1<<(i-1)); |
| if (bit != 0 && buffer.length()>0) buffer.append(" | "); //$NON-NLS-1$ |
| switch (bit) { |
| case IJavaSearchConstants.FIELD_DECLARATION_TYPE_REFERENCE: |
| buffer.append("FIELD_DECLARATION_TYPE_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.LOCAL_VARIABLE_DECLARATION_TYPE_REFERENCE: |
| buffer.append("LOCAL_VARIABLE_DECLARATION_TYPE_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.PARAMETER_DECLARATION_TYPE_REFERENCE: |
| buffer.append("PARAMETER_DECLARATION_TYPE_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.SUPERTYPE_TYPE_REFERENCE: |
| buffer.append("SUPERTYPE_TYPE_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.THROWS_CLAUSE_TYPE_REFERENCE: |
| buffer.append("THROWS_CLAUSE_TYPE_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.CAST_TYPE_REFERENCE: |
| buffer.append("CAST_TYPE_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.CATCH_TYPE_REFERENCE: |
| buffer.append("CATCH_TYPE_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.CLASS_INSTANCE_CREATION_TYPE_REFERENCE: |
| buffer.append("CLASS_INSTANCE_CREATION_TYPE_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.RETURN_TYPE_REFERENCE: |
| buffer.append("RETURN_TYPE_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.IMPORT_DECLARATION_TYPE_REFERENCE: |
| buffer.append("IMPORT_DECLARATION_TYPE_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.ANNOTATION_TYPE_REFERENCE: |
| buffer.append("ANNOTATION_TYPE_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.TYPE_ARGUMENT_TYPE_REFERENCE: |
| buffer.append("TYPE_ARGUMENT_TYPE_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.TYPE_VARIABLE_BOUND_TYPE_REFERENCE: |
| buffer.append("TYPE_VARIABLE_BOUND_TYPE_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.WILDCARD_BOUND_TYPE_REFERENCE: |
| buffer.append("WILDCARD_BOUND_TYPE_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.SUPER_REFERENCE: |
| buffer.append("SUPER_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.QUALIFIED_REFERENCE: |
| buffer.append("QUALIFIED_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.THIS_REFERENCE: |
| buffer.append("THIS_REFERENCE"); //$NON-NLS-1$ |
| break; |
| case IJavaSearchConstants.IMPLICIT_THIS_REFERENCE: |
| buffer.append("IMPLICIT_THIS_REFERENCE"); //$NON-NLS-1$ |
| break; |
| } |
| } |
| return buffer.toString(); |
| } |
| |
| public SearchPattern getBlankPattern() { |
| return null; |
| } |
| |
| final int getMatchMode() { |
| return this.matchMode; |
| } |
| |
| final boolean isCamelCase() { |
| return this.isCamelCase; |
| } |
| |
| final boolean isCaseSensitive () { |
| return this.isCaseSensitive; |
| } |
| |
| final boolean isErasureMatch() { |
| return (this.matchCompatibility & R_ERASURE_MATCH) != 0; |
| } |
| |
| final boolean isEquivalentMatch() { |
| return (this.matchCompatibility & R_EQUIVALENT_MATCH) != 0; |
| } |
| |
| /* |
| * Extract method arguments using unique key for parameterized methods |
| * and type parameters for non-generic ones. |
| */ |
| char[][] extractMethodArguments(IMethod method) { |
| |
| // Use bind key if the element is resolved |
| if (method.isResolved()) { |
| BindingKey bindingKey = new BindingKey(method.getKey()); |
| if (bindingKey.isParameterizedMethod()) { |
| String[] argumentsSignatures = bindingKey.getTypeArguments(); |
| int length = argumentsSignatures.length; |
| if (length > 0) { |
| char[][] methodArguments = new char[length][]; |
| for (int i=0; i<length; i++) { |
| methodArguments[i] = argumentsSignatures[i].toCharArray(); |
| CharOperation.replace(methodArguments[i], new char[] { '$', '/' }, '.'); |
| } |
| return methodArguments; |
| } |
| } |
| return null; |
| } |
| |
| // Try to get the argument using the JavaModel info |
| try { |
| ITypeParameter[] parameters = method.getTypeParameters(); |
| if (parameters != null) { |
| int length = parameters.length; |
| if (length > 0) { |
| char[][] arguments = new char[length][]; |
| for (int i=0; i<length; i++) { |
| arguments[i] = Signature.createTypeSignature(parameters[i].getElementName(), false).toCharArray(); |
| } |
| return arguments; |
| } |
| } |
| } |
| catch (JavaModelException jme) { |
| // do nothing |
| } |
| return null; |
| } |
| |
| /** |
| * @return Returns the typeArguments. |
| */ |
| final char[][][] getTypeArguments() { |
| return this.typeArguments; |
| } |
| |
| /** |
| * Returns whether the pattern has signatures or not. |
| * If pattern {@link #typeArguments} field, this field shows that it was built |
| * on a generic source type. |
| * @return true if {@link #typeSignatures} field is not null and has a length greater than 0. |
| */ |
| public final boolean hasSignatures() { |
| return this.typeSignatures != null && this.typeSignatures.length > 0; |
| } |
| |
| /** |
| * Returns whether the pattern includes type arguments information or not. |
| * @return default is false |
| */ |
| public final boolean hasTypeArguments() { |
| return (this.flags & HAS_TYPE_ARGUMENTS) != 0; |
| } |
| |
| /** |
| * Returns whether the pattern includes type parameters information or not. |
| * @return true if {@link #typeArguments} contains type parameters instead |
| * type arguments signatures. |
| */ |
| public final boolean hasTypeParameters() { |
| return !hasSignatures() && hasTypeArguments(); |
| } |
| |
| /** |
| * Return whether two suffixes are compatible. |
| * |
| * Note that obvious compatibility values as equals and {@link IIndexConstants#TYPE_SUFFIX} |
| * has to be tested by caller to avoid unnecessary method call... |
| * |
| * @param typeSuffix |
| * @param patternSuffix |
| * @return true if suffixes are compatible, false otherwise |
| */ |
| boolean matchDifferentTypeSuffixes(int typeSuffix, int patternSuffix) { |
| switch(typeSuffix) { |
| case CLASS_SUFFIX : |
| switch (patternSuffix) { |
| case CLASS_AND_INTERFACE_SUFFIX : |
| case CLASS_AND_ENUM_SUFFIX : |
| return true; |
| } |
| return false; |
| |
| case INTERFACE_SUFFIX : |
| switch (patternSuffix) { |
| case CLASS_AND_INTERFACE_SUFFIX : |
| case INTERFACE_AND_ANNOTATION_SUFFIX: |
| return true; |
| } |
| return false; |
| |
| case ENUM_SUFFIX : |
| return patternSuffix == CLASS_AND_ENUM_SUFFIX; |
| |
| case ANNOTATION_TYPE_SUFFIX : |
| return patternSuffix == INTERFACE_AND_ANNOTATION_SUFFIX; |
| |
| case CLASS_AND_INTERFACE_SUFFIX : |
| switch (patternSuffix) { |
| case CLASS_SUFFIX : |
| case INTERFACE_SUFFIX : |
| return true; |
| } |
| return false; |
| |
| case CLASS_AND_ENUM_SUFFIX : |
| switch (patternSuffix) { |
| case CLASS_SUFFIX : |
| case ENUM_SUFFIX : |
| return true; |
| } |
| return false; |
| |
| case INTERFACE_AND_ANNOTATION_SUFFIX : |
| switch (patternSuffix) { |
| case INTERFACE_SUFFIX : |
| case ANNOTATION_TYPE_SUFFIX : |
| return true; |
| } |
| return false; |
| } |
| |
| // Default behavior is to match suffixes |
| return true; |
| } |
| |
| protected StringBuffer print(StringBuffer output) { |
| output.append(", "); //$NON-NLS-1$ |
| if (hasTypeArguments() && hasSignatures()) { |
| output.append("signature:\""); //$NON-NLS-1$ |
| output.append(this.typeSignatures[0]); |
| output.append("\", "); //$NON-NLS-1$ |
| } |
| switch(getMatchMode()) { |
| case R_EXACT_MATCH : |
| output.append("exact match, "); //$NON-NLS-1$ |
| break; |
| case R_PREFIX_MATCH : |
| output.append("prefix match, "); //$NON-NLS-1$ |
| break; |
| case R_PATTERN_MATCH : |
| output.append("pattern match, "); //$NON-NLS-1$ |
| break; |
| case R_REGEXP_MATCH : |
| output.append("regexp match, "); //$NON-NLS-1$ |
| break; |
| case R_CAMELCASE_MATCH : |
| output.append("camel case match, "); //$NON-NLS-1$ |
| break; |
| case R_CAMELCASE_SAME_PART_COUNT_MATCH: |
| output.append("camel case same part count match, "); //$NON-NLS-1$ |
| break; |
| } |
| if (isCaseSensitive()) |
| output.append("case sensitive, "); //$NON-NLS-1$ |
| else |
| output.append("case insensitive, "); //$NON-NLS-1$ |
| if ((this.matchCompatibility & R_FULL_MATCH) != 0) { |
| output.append("generic full match, "); //$NON-NLS-1$ |
| } |
| if ((this.matchCompatibility & R_ERASURE_MATCH) != 0) { |
| output.append("generic erasure match, "); //$NON-NLS-1$ |
| } |
| if ((this.matchCompatibility & R_EQUIVALENT_MATCH) != 0) { |
| output.append("generic equivalent match, "); //$NON-NLS-1$ |
| } |
| output.append("fine grain: "); //$NON-NLS-1$ |
| output.append(getFineGrainFlagString(this.fineGrain)); |
| return output; |
| } |
| |
| /** |
| * @param typeArguments The typeArguments to set. |
| */ |
| final void setTypeArguments(char[][][] typeArguments) { |
| this.typeArguments = typeArguments; |
| // update flags |
| if (this.typeArguments != null) { |
| int length = this.typeArguments.length; |
| for (int i=0; i<length; i++) { |
| if (this.typeArguments[i] != null && this.typeArguments[i].length > 0) { |
| this.flags |= HAS_TYPE_ARGUMENTS; |
| break; |
| } |
| } |
| } |
| } |
| |
| /* |
| * Extract and store type signatures and arguments using unique key for parameterized types |
| * and type parameters for non-generic ones |
| */ |
| void storeTypeSignaturesAndArguments(IType type) { |
| if (type.isResolved()) { |
| BindingKey bindingKey = new BindingKey(type.getKey()); |
| if (bindingKey.isParameterizedType() || bindingKey.isRawType()) { |
| String signature = bindingKey.toSignature(); |
| this.typeSignatures = Util.splitTypeLevelsSignature(signature); |
| setTypeArguments(Util.getAllTypeArguments(this.typeSignatures)); |
| } |
| return; |
| } |
| |
| // Scan hierarchy to store type arguments at each level |
| char[][][] typeParameters = new char[10][][]; |
| int ptr = -1; |
| boolean hasParameters = false; |
| try { |
| IJavaElement parent = type; |
| ITypeParameter[] parameters = null; |
| while (parent != null && parent.getElementType() == IJavaElement.TYPE) { |
| if (++ptr > typeParameters.length) { |
| System.arraycopy(typeParameters, 0, typeParameters = new char[typeParameters.length+10][][], 0, ptr); |
| } |
| IType parentType = (IType) parent; |
| parameters = parentType.getTypeParameters(); |
| if (parameters !=null) { |
| int length = parameters.length; |
| if (length > 0) { |
| hasParameters = true; |
| typeParameters[ptr] = new char[length][]; |
| for (int i=0; i<length; i++) |
| typeParameters[ptr][i] = Signature.createTypeSignature(parameters[i].getElementName(), false).toCharArray(); |
| } |
| } |
| parent = parent.getParent(); |
| } |
| } |
| catch (JavaModelException jme) { |
| return; |
| } |
| |
| // Store type arguments if any |
| if (hasParameters) { |
| if (++ptr < typeParameters.length) |
| System.arraycopy(typeParameters, 0, typeParameters = new char[ptr][][], 0, ptr); |
| setTypeArguments(typeParameters); |
| } |
| } |
| public final String toString() { |
| return print(new StringBuffer(30)).toString(); |
| } |
| } |