001: /*******************************************************************************
002: * Copyright (c) 2004, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.core.search.matching;
011:
012: import org.eclipse.jdt.core.BindingKey;
013: import org.eclipse.jdt.core.IJavaElement;
014: import org.eclipse.jdt.core.IMethod;
015: import org.eclipse.jdt.core.IType;
016: import org.eclipse.jdt.core.ITypeParameter;
017: import org.eclipse.jdt.core.JavaModelException;
018: import org.eclipse.jdt.core.Signature;
019: import org.eclipse.jdt.core.compiler.CharOperation;
020: import org.eclipse.jdt.core.search.SearchPattern;
021: import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants;
022: import org.eclipse.jdt.internal.core.util.Util;
023:
024: public class JavaSearchPattern extends SearchPattern implements
025: IIndexConstants {
026:
027: /*
028: * Whether this pattern is case sensitive.
029: */
030: boolean isCaseSensitive;
031:
032: /*
033: * Whether this pattern is camel case.
034: */
035: boolean isCamelCase;
036:
037: /**
038: * One of following pattern value:
039: * <ul>
040: * <li>{@link #R_EXACT_MATCH}</li>
041: * <li>{@link #R_PREFIX_MATCH}</li>
042: * <li>{@link #R_PATTERN_MATCH}</li>
043: * <li>{@link #R_REGEXP_MATCH}</li>
044: * <li>{@link #R_CAMEL_CASE_MATCH}</li>
045: * </ul>
046: */
047: int matchMode;
048:
049: /**
050: * One of {@link #R_ERASURE_MATCH}, {@link #R_EQUIVALENT_MATCH}, {@link #R_FULL_MATCH}.
051: */
052: int matchCompatibility;
053:
054: /**
055: * Mask used on match rule for match mode.
056: */
057: public static final int MATCH_MODE_MASK = R_EXACT_MATCH
058: | R_PREFIX_MATCH | R_PATTERN_MATCH | R_REGEXP_MATCH;
059:
060: /**
061: * Mask used on match rule for generic relevance.
062: */
063: public static final int MATCH_COMPATIBILITY_MASK = R_ERASURE_MATCH
064: | R_EQUIVALENT_MATCH | R_FULL_MATCH;
065:
066: // Signatures and arguments for parameterized types search
067: char[][] typeSignatures;
068: private char[][][] typeArguments;
069: private int flags = 0;
070: static final int HAS_TYPE_ARGUMENTS = 1;
071:
072: protected JavaSearchPattern(int patternKind, int matchRule) {
073: super (matchRule);
074: ((InternalSearchPattern) this ).kind = patternKind;
075: // Use getMatchRule() instead of matchRule as super constructor may modify its value
076: // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=81377
077: int rule = getMatchRule();
078: this .isCaseSensitive = (rule & R_CASE_SENSITIVE) != 0;
079: this .isCamelCase = (rule & R_CAMEL_CASE_MATCH) != 0;
080: this .matchCompatibility = rule & MATCH_COMPATIBILITY_MASK;
081: this .matchMode = rule & MATCH_MODE_MASK;
082: }
083:
084: public SearchPattern getBlankPattern() {
085: return null;
086: }
087:
088: final int getMatchMode() {
089: return this .matchMode;
090: }
091:
092: final boolean isCamelCase() {
093: return this .isCamelCase;
094: }
095:
096: final boolean isCaseSensitive() {
097: return this .isCaseSensitive;
098: }
099:
100: final boolean isErasureMatch() {
101: return (this .matchCompatibility & R_ERASURE_MATCH) != 0;
102: }
103:
104: final boolean isEquivalentMatch() {
105: return (this .matchCompatibility & R_EQUIVALENT_MATCH) != 0;
106: }
107:
108: /*
109: * Extract method arguments using unique key for parameterized methods
110: * and type parameters for non-generic ones.
111: */
112: char[][] extractMethodArguments(IMethod method) {
113: String[] argumentsSignatures = null;
114: BindingKey key;
115: if (method.isResolved()
116: && (key = new BindingKey(method.getKey()))
117: .isParameterizedType()) {
118: argumentsSignatures = key.getTypeArguments();
119: } else {
120: try {
121: ITypeParameter[] parameters = method
122: .getTypeParameters();
123: if (parameters != null) {
124: int length = parameters.length;
125: if (length > 0) {
126: char[][] arguments = new char[length][];
127: for (int i = 0; i < length; i++) {
128: arguments[i] = Signature
129: .createTypeSignature(
130: parameters[i]
131: .getElementName(),
132: false).toCharArray();
133: }
134: return arguments;
135: }
136: }
137: } catch (JavaModelException jme) {
138: // do nothing
139: }
140: return null;
141: }
142:
143: // Parameterized method
144: if (argumentsSignatures != null) {
145: int length = argumentsSignatures.length;
146: if (length > 0) {
147: char[][] methodArguments = new char[length][];
148: for (int i = 0; i < length; i++) {
149: methodArguments[i] = argumentsSignatures[i]
150: .toCharArray();
151: CharOperation.replace(methodArguments[i],
152: new char[] { '$', '/' }, '.');
153: }
154: return methodArguments;
155: }
156: }
157: return null;
158: }
159:
160: /**
161: * @return Returns the typeArguments.
162: */
163: final char[][][] getTypeArguments() {
164: return typeArguments;
165: }
166:
167: /**
168: * Returns whether the pattern has signatures or not.
169: * If pattern {@link #typeArguments} field, this field shows that it was built
170: * on a generic source type.
171: * @return true if {@link #typeSignatures} field is not null and has a length greater than 0.
172: */
173: public final boolean hasSignatures() {
174: return this .typeSignatures != null
175: && this .typeSignatures.length > 0;
176: }
177:
178: /**
179: * Returns whether the pattern includes type arguments information or not.
180: * @return default is false
181: */
182: public final boolean hasTypeArguments() {
183: return (this .flags & HAS_TYPE_ARGUMENTS) != 0;
184: }
185:
186: /**
187: * Returns whether the pattern includes type parameters information or not.
188: * @return true if {@link #typeArguments} contains type parameters instead
189: * type arguments signatures.
190: */
191: public final boolean hasTypeParameters() {
192: return !hasSignatures() && hasTypeArguments();
193: }
194:
195: /**
196: * Return whether two suffixes are compatible.
197: *
198: * Note that obvious compatibility values as equals and {@link IIndexConstants#TYPE_SUFFIX}
199: * has to be tested by caller to avoid unnecessary method call...
200: *
201: * @param typeSuffix
202: * @param patternSuffix
203: * @return true if suffixes are compatible, false otherwise
204: */
205: boolean matchDifferentTypeSuffixes(int typeSuffix, int patternSuffix) {
206: switch (typeSuffix) {
207: case CLASS_SUFFIX:
208: switch (patternSuffix) {
209: case CLASS_AND_INTERFACE_SUFFIX:
210: case CLASS_AND_ENUM_SUFFIX:
211: return true;
212: }
213: return false;
214:
215: case INTERFACE_SUFFIX:
216: switch (patternSuffix) {
217: case CLASS_AND_INTERFACE_SUFFIX:
218: case INTERFACE_AND_ANNOTATION_SUFFIX:
219: return true;
220: }
221: return false;
222:
223: case ENUM_SUFFIX:
224: return patternSuffix == CLASS_AND_ENUM_SUFFIX;
225:
226: case ANNOTATION_TYPE_SUFFIX:
227: return patternSuffix == INTERFACE_AND_ANNOTATION_SUFFIX;
228:
229: case CLASS_AND_INTERFACE_SUFFIX:
230: switch (patternSuffix) {
231: case CLASS_SUFFIX:
232: case INTERFACE_SUFFIX:
233: return true;
234: }
235: return false;
236:
237: case CLASS_AND_ENUM_SUFFIX:
238: switch (patternSuffix) {
239: case CLASS_SUFFIX:
240: case ENUM_SUFFIX:
241: return true;
242: }
243: return false;
244:
245: case INTERFACE_AND_ANNOTATION_SUFFIX:
246: switch (patternSuffix) {
247: case INTERFACE_SUFFIX:
248: case ANNOTATION_TYPE_SUFFIX:
249: return true;
250: }
251: return false;
252: }
253:
254: // Default behavior is to match suffixes
255: return true;
256: }
257:
258: protected StringBuffer print(StringBuffer output) {
259: output.append(", "); //$NON-NLS-1$
260: if (hasTypeArguments() && hasSignatures()) {
261: output.append("signature:\""); //$NON-NLS-1$
262: output.append(this .typeSignatures[0]);
263: output.append("\", "); //$NON-NLS-1$
264: }
265: if (this .isCamelCase) {
266: output.append("camel case + "); //$NON-NLS-1$
267: }
268: switch (getMatchMode()) {
269: case R_EXACT_MATCH:
270: output.append("exact match,"); //$NON-NLS-1$
271: break;
272: case R_PREFIX_MATCH:
273: output.append("prefix match,"); //$NON-NLS-1$
274: break;
275: case R_PATTERN_MATCH:
276: output.append("pattern match,"); //$NON-NLS-1$
277: break;
278: case R_REGEXP_MATCH:
279: output.append("regexp match, "); //$NON-NLS-1$
280: break;
281: }
282: if (isCaseSensitive())
283: output.append(" case sensitive"); //$NON-NLS-1$
284: else
285: output.append(" case insensitive"); //$NON-NLS-1$
286: if ((this .matchCompatibility & R_ERASURE_MATCH) != 0) {
287: output.append(", erasure only"); //$NON-NLS-1$
288: }
289: if ((this .matchCompatibility & R_EQUIVALENT_MATCH) != 0) {
290: output.append(", equivalent oronly"); //$NON-NLS-1$
291: }
292: return output;
293: }
294:
295: /**
296: * @param typeArguments The typeArguments to set.
297: */
298: final void setTypeArguments(char[][][] typeArguments) {
299: this .typeArguments = typeArguments;
300: // update flags
301: if (this .typeArguments != null) {
302: int length = this .typeArguments.length;
303: for (int i = 0; i < length; i++) {
304: if (this .typeArguments[i] != null
305: && this .typeArguments[i].length > 0) {
306: this .flags |= HAS_TYPE_ARGUMENTS;
307: break;
308: }
309: }
310: }
311: }
312:
313: /*
314: * Extract and store type signatures and arguments using unique key for parameterized types
315: * and type parameters for non-generic ones
316: */
317: void storeTypeSignaturesAndArguments(IType type) {
318: BindingKey key;
319: if (type.isResolved()
320: && (key = new BindingKey(type.getKey()))
321: .isParameterizedType()) {
322: String signature = key.toSignature();
323: this .typeSignatures = Util
324: .splitTypeLevelsSignature(signature);
325: setTypeArguments(Util
326: .getAllTypeArguments(this .typeSignatures));
327: } else {
328: // Scan hierachy to store type arguments at each level
329: char[][][] typeParameters = new char[10][][];
330: int ptr = -1;
331: boolean hasParameters = false;
332: try {
333: IJavaElement parent = type;
334: ITypeParameter[] parameters = null;
335: while (parent != null
336: && parent.getElementType() == IJavaElement.TYPE) {
337: if (++ptr > typeParameters.length) {
338: System
339: .arraycopy(
340: typeParameters,
341: 0,
342: typeParameters = new char[typeParameters.length + 10][][],
343: 0, ptr);
344: }
345: IType parentType = (IType) parent;
346: parameters = parentType.getTypeParameters();
347: if (parameters != null) {
348: int length = parameters.length;
349: if (length > 0) {
350: hasParameters = true;
351: typeParameters[ptr] = new char[length][];
352: for (int i = 0; i < length; i++)
353: typeParameters[ptr][i] = Signature
354: .createTypeSignature(
355: parameters[i]
356: .getElementName(),
357: false).toCharArray();
358: }
359: }
360: parent = parent.getParent();
361: }
362: } catch (JavaModelException jme) {
363: return;
364: }
365: // Store type arguments if any
366: if (hasParameters) {
367: if (++ptr < typeParameters.length)
368: System.arraycopy(typeParameters, 0,
369: typeParameters = new char[ptr][][], 0, ptr);
370: setTypeArguments(typeParameters);
371: }
372: }
373: }
374:
375: public final String toString() {
376: return print(new StringBuffer(30)).toString();
377: }
378: }
|