KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > core > search > SearchPattern


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.core.search;
12
13 import org.eclipse.jdt.core.*;
14 import org.eclipse.jdt.core.compiler.*;
15 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
16 import org.eclipse.jdt.internal.compiler.parser.Scanner;
17 import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
18 import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
19 import org.eclipse.jdt.internal.core.LocalVariable;
20 import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants;
21 import org.eclipse.jdt.internal.core.search.matching.*;
22
23
24 /**
25  * A search pattern defines how search results are found. Use <code>SearchPattern.createPattern</code>
26  * to create a search pattern.
27  * <p>
28  * Search patterns are used during the search phase to decode index entries that were added during the indexing phase
29  * (see {@link SearchDocument#addIndexEntry(char[], char[])}). When an index is queried, the
30  * index categories and keys to consider are retrieved from the search pattern using {@link #getIndexCategories()} and
31  * {@link #getIndexKey()}, as well as the match rule (see {@link #getMatchRule()}). A blank pattern is
32  * then created (see {@link #getBlankPattern()}). This blank pattern is used as a record as follows.
33  * For each index entry in the given index categories and that starts with the given key, the blank pattern is fed using
34  * {@link #decodeIndexKey(char[])}. The original pattern is then asked if it matches the decoded key using
35  * {@link #matchesDecodedKey(SearchPattern)}. If it matches, a search doument is created for this index entry
36  * using {@link SearchParticipant#getDocument(String)}.
37  *
38  * </p><p>
39  * This class is intended to be subclassed by clients. A default behavior is provided for each of the methods above, that
40  * clients can ovveride if they wish.
41  * </p>
42  * @see #createPattern(org.eclipse.jdt.core.IJavaElement, int)
43  * @see #createPattern(String, int, int, int)
44  * @since 3.0
45  */

46 public abstract class SearchPattern extends InternalSearchPattern {
47
48     // Rules for pattern matching: (exact, prefix, pattern) [ | case sensitive]
49
/**
50      * Match rule: The search pattern matches exactly the search result,
51      * that is, the source of the search result equals the search pattern.
52      */

53     public static final int R_EXACT_MATCH = 0;
54
55     /**
56      * Match rule: The search pattern is a prefix of the search result.
57      */

58     public static final int R_PREFIX_MATCH = 0x0001;
59
60     /**
61      * Match rule: The search pattern contains one or more wild cards ('*' or '?').
62      * A '*' wild-card can replace 0 or more characters in the search result.
63      * A '?' wild-card replaces exactly 1 character in the search result.
64      */

65     public static final int R_PATTERN_MATCH = 0x0002;
66
67     /**
68      * Match rule: The search pattern contains a regular expression.
69      */

70     public static final int R_REGEXP_MATCH = 0x0004;
71
72     /**
73      * Match rule: The search pattern matches the search result only if cases are the same.
74      * Can be combined to previous rules, e.g. {@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE}
75      */

76     public static final int R_CASE_SENSITIVE = 0x0008;
77
78     /**
79      * Match rule: The search pattern matches search results as raw/parameterized types/methods with same erasure.
80      * This mode has no effect on other java elements search.<br>
81      * Type search example:
82      * <ul>
83      * <li>pattern: <code>List&lt;Exception&gt;</code></li>
84      * <li>match: <code>List&lt;Object&gt;</code></li>
85      * </ul>
86      * Method search example:
87      * <ul>
88      * <li>declaration: <code>&lt;T&gt;foo(T t)</code></li>
89      * <li>pattern: <code>&lt;Exception&gt;foo(new Exception())</code></li>
90      * <li>match: <code>&lt;Object&gt;foo(new Object())</code></li>
91      * </ul>
92      * Can be combined to all other match rules, e.g. {@link #R_CASE_SENSITIVE} | {@link #R_ERASURE_MATCH}
93      * This rule is not activated by default, so raw types or parameterized types with same erasure will not be found
94      * for pattern List&lt;String&gt;,
95      * Note that with this pattern, the match selection will be only on the erasure even for parameterized types.
96      * @since 3.1
97      */

98     public static final int R_ERASURE_MATCH = 0x0010;
99
100     /**
101      * Match rule: The search pattern matches search results as raw/parameterized types/methods with equivalent type parameters.
102      * This mode has no effect on other java elements search.<br>
103      * Type search example:
104      * <ul>
105      * <li>pattern: <code>List&lt;Exception&gt;</code></li>
106      * <li>match:
107      * <ul>
108      * <li><code>List&lt;? extends Throwable&gt;</code></li>
109      * <li><code>List&lt;? super RuntimeException&gt;</code></li>
110      * <li><code>List&lt;?&gt;</code></li>
111      * </ul>
112      * </li>
113      * </ul>
114      * Method search example:
115      * <ul>
116      * <li>declaration: <code>&lt;T&gt;foo(T t)</code></li>
117      * <li>pattern: <code>&lt;Exception&gt;foo(new Exception())</code></li>
118      * <li>match:
119      * <ul>
120      * <li><code>&lt;? extends Throwable&gt;foo(new Exception())</code></li>
121      * <li><code>&lt;? super RuntimeException&gt;foo(new Exception())</code></li>
122      * <li><code>foo(new Exception())</code></li>
123      * </ul>
124      * </ul>
125      * Can be combined to all other match rules, e.g. {@link #R_CASE_SENSITIVE} | {@link #R_EQUIVALENT_MATCH}
126      * This rule is not activated by default, so raw types or equivalent parameterized types will not be found
127      * for pattern List&lt;String&gt;,
128      * This mode is overridden by {@link #R_ERASURE_MATCH} as erasure matches obviously include equivalent ones.
129      * That means that pattern with rule set to {@link #R_EQUIVALENT_MATCH} | {@link #R_ERASURE_MATCH}
130      * will return same results than rule only set with {@link #R_ERASURE_MATCH}.
131      * @since 3.1
132      */

133     public static final int R_EQUIVALENT_MATCH = 0x0020;
134
135     /**
136      * Match rule: The search pattern matches exactly the search result,
137      * that is, the source of the search result equals the search pattern.
138      * @since 3.1
139      */

140     public static final int R_FULL_MATCH = 0x0040;
141
142     /**
143      * Match rule: The search pattern contains a Camel Case expression.
144      * <br>
145      * Examples:
146      * <ul>
147      * <li><code>NPE</code> type string pattern will match
148      * <code>NullPointerException</code> and <code>NpPermissionException</code> types,</li>
149      * <li><code>NuPoEx</code> type string pattern will only match
150      * <code>NullPointerException</code> type.</li>
151      * </ul>
152      * @see CharOperation#camelCaseMatch(char[], char[]) for a detailed explanation
153      * of Camel Case matching.
154      *<br>
155      * Can be combined to {@link #R_PREFIX_MATCH} match rule. For example,
156      * when prefix match rule is combined with Camel Case match rule,
157      * <code>"nPE"</code> pattern will match <code>nPException</code>.
158      *<br>
159      * Match rule {@link #R_PATTERN_MATCH} may also be combined but both rules
160      * will not be used simultaneously as they are mutually exclusive.
161      * Used match rule depends on whether string pattern contains specific pattern
162      * characters (e.g. '*' or '?') or not. If it does, then only Pattern match rule
163      * will be used, otherwise only Camel Case match will be used.
164      * For example, with <code>"NPE"</code> string pattern, search will only use
165      * Camel Case match rule, but with <code>N*P*E*</code> string pattern, it will
166      * use only Pattern match rule.
167      *
168      * @since 3.2
169      */

170     public static final int R_CAMELCASE_MATCH = 0x0080;
171
172     private static final int MODE_MASK = R_EXACT_MATCH | R_PREFIX_MATCH | R_PATTERN_MATCH | R_REGEXP_MATCH;
173
174     private int matchRule;
175
176 /**
177  * Creates a search pattern with the rule to apply for matching index keys.
178  * It can be exact match, prefix match, pattern match or regexp match.
179  * Rule can also be combined with a case sensitivity flag.
180  *
181  * @param matchRule one of {@link #R_EXACT_MATCH}, {@link #R_PREFIX_MATCH}, {@link #R_PATTERN_MATCH},
182  * {@link #R_REGEXP_MATCH}, {@link #R_CAMELCASE_MATCH} combined with one of following values:
183  * {@link #R_CASE_SENSITIVE}, {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH}.
184  * e.g. {@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE} if an exact and case sensitive match is requested,
185  * {@link #R_PREFIX_MATCH} if a prefix non case sensitive match is requested or {@link #R_EXACT_MATCH} | {@link #R_ERASURE_MATCH}
186  * if a non case sensitive and erasure match is requested.<br>
187  * Note that {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH} have no effect
188  * on non-generic types/methods search.<br>
189  * Note also that default behavior for generic types/methods search is to find exact matches.
190  */

191 public SearchPattern(int matchRule) {
192     this.matchRule = matchRule;
193     // Set full match implicit mode
194
if ((matchRule & (R_EQUIVALENT_MATCH | R_ERASURE_MATCH )) == 0) {
195         this.matchRule |= R_FULL_MATCH;
196     }
197 }
198
199 /**
200  * Answers true if the pattern matches the given name using CamelCase rules, or false otherwise.
201  * CamelCase matching does NOT accept explicit wild-cards '*' and '?' and is inherently case sensitive.
202  * <br>
203  * CamelCase denotes the convention of writing compound names without spaces, and capitalizing every term.
204  * This function recognizes both upper and lower CamelCase, depending whether the leading character is capitalized
205  * or not. The leading part of an upper CamelCase pattern is assumed to contain a sequence of capitals which are appearing
206  * in the matching name; e.g. 'NPE' will match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern
207  * uses a lowercase first character. In Java, type names follow the upper CamelCase convention, whereas method or field
208  * names follow the lower CamelCase convention.
209  * <br>
210  * The pattern may contain lowercase characters, which will be match in a case sensitive way. These characters must
211  * appear in sequence in the name. For instance, 'NPExcep' will match 'NullPointerException', but not 'NullPointerExCEPTION'
212  * or 'NuPoEx' will match 'NullPointerException', but not 'NoPointerException'.
213  * <br><br>
214  * Examples:
215  * <ol>
216  * <li><pre>
217  * pattern = "NPE"
218  * name = NullPointerException / NoPermissionException
219  * result => true
220  * </pre>
221  * </li>
222  * <li><pre>
223  * pattern = "NuPoEx"
224  * name = NullPointerException
225  * result => true
226  * </pre>
227  * </li>
228  * <li><pre>
229  * pattern = "npe"
230  * name = NullPointerException
231  * result => false
232  * </pre>
233  * </li>
234  * </ol>
235  * @see CharOperation#camelCaseMatch(char[], char[])
236  * Implementation has been entirely copied from this method except for array lengthes
237  * which were obviously replaced with calls to {@link String#length()}.
238  *
239  * @param pattern the given pattern
240  * @param name the given name
241  * @return true if the pattern matches the given name, false otherwise
242  * @since 3.2
243  */

244 public static final boolean camelCaseMatch(String JavaDoc pattern, String JavaDoc name) {
245     if (pattern == null)
246         return true; // null pattern is equivalent to '*'
247
if (name == null)
248         return false; // null name cannot match
249

250     return camelCaseMatch(pattern, 0, pattern.length(), name, 0, name.length());
251 }
252
253 /**
254  * Answers true if a sub-pattern matches the subpart of the given name using CamelCase rules, or false otherwise.
255  * CamelCase matching does NOT accept explicit wild-cards '*' and '?' and is inherently case sensitive.
256  * Can match only subset of name/pattern, considering end positions as non-inclusive.
257  * The subpattern is defined by the patternStart and patternEnd positions.
258  * <br>
259  * CamelCase denotes the convention of writing compound names without spaces, and capitalizing every term.
260  * This function recognizes both upper and lower CamelCase, depending whether the leading character is capitalized
261  * or not. The leading part of an upper CamelCase pattern is assumed to contain a sequence of capitals which are appearing
262  * in the matching name; e.g. 'NPE' will match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern
263  * uses a lowercase first character. In Java, type names follow the upper CamelCase convention, whereas method or field
264  * names follow the lower CamelCase convention.
265  * <br>
266  * The pattern may contain lowercase characters, which will be match in a case sensitive way. These characters must
267  * appear in sequence in the name. For instance, 'NPExcep' will match 'NullPointerException', but not 'NullPointerExCEPTION'
268  * or 'NuPoEx' will match 'NullPointerException', but not 'NoPointerException'.
269  * <br><br>
270  * Examples:
271  * <ol>
272  * <li><pre>
273  * pattern = "NPE"
274  * patternStart = 0
275  * patternEnd = 3
276  * name = NullPointerException
277  * nameStart = 0
278  * nameEnd = 20
279  * result => true
280  * </pre>
281  * </li>
282  * <li><pre>
283  * pattern = "NPE"
284  * patternStart = 0
285  * patternEnd = 3
286  * name = NoPermissionException
287  * nameStart = 0
288  * nameEnd = 21
289  * result => true
290  * </pre>
291  * </li>
292  * <li><pre>
293  * pattern = "NuPoEx"
294  * patternStart = 0
295  * patternEnd = 6
296  * name = NullPointerException
297  * nameStart = 0
298  * nameEnd = 20
299  * result => true
300  * </pre>
301  * </li>
302  * <li><pre>
303  * pattern = "NuPoEx"
304  * patternStart = 0
305  * patternEnd = 6
306  * name = NoPermissionException
307  * nameStart = 0
308  * nameEnd = 21
309  * result => false
310  * </pre>
311  * </li>
312  * <li><pre>
313  * pattern = "npe"
314  * patternStart = 0
315  * patternEnd = 3
316  * name = NullPointerException
317  * nameStart = 0
318  * nameEnd = 20
319  * result => false
320  * </pre>
321  * </li>
322  * </ol>
323  * @see CharOperation#camelCaseMatch(char[], int, int, char[], int, int)
324  * Implementation has been entirely copied from this method except for array lengthes
325  * which were obviously replaced with calls to {@link String#length()} and
326  * for array direct access which were replaced with calls to {@link String#charAt(int)}.
327  *
328  * @param pattern the given pattern
329  * @param patternStart the start index of the pattern, inclusive
330  * @param patternEnd the end index of the pattern, exclusive
331  * @param name the given name
332  * @param nameStart the start index of the name, inclusive
333  * @param nameEnd the end index of the name, exclusive
334  * @return true if a sub-pattern matches the subpart of the given name, false otherwise
335  * @since 3.2
336  */

337 public static final boolean camelCaseMatch(String JavaDoc pattern, int patternStart, int patternEnd, String JavaDoc name, int nameStart, int nameEnd) {
338     if (name == null)
339         return false; // null name cannot match
340
if (pattern == null)
341         return true; // null pattern is equivalent to '*'
342
if (patternEnd < 0) patternEnd = pattern.length();
343     if (nameEnd < 0) nameEnd = name.length();
344
345     if (patternEnd <= patternStart) return nameEnd <= nameStart;
346     if (nameEnd <= nameStart) return false;
347     // check first pattern char
348
if (name.charAt(nameStart) != pattern.charAt(patternStart)) {
349         // first char must strictly match (upper/lower)
350
return false;
351     }
352
353     char patternChar, nameChar;
354     int iPattern = patternStart;
355     int iName = nameStart;
356
357     // Main loop is on pattern characters
358
while (true) {
359
360         iPattern++;
361         iName++;
362
363         if (iPattern == patternEnd) {
364             // We have exhausted pattern, so it's a match
365
return true;
366         }
367
368         if (iName == nameEnd){
369             // We have exhausted name (and not pattern), so it's not a match
370
return false;
371         }
372
373         // For as long as we're exactly matching, bring it on (even if it's a lower case character)
374
if ((patternChar = pattern.charAt(iPattern)) == name.charAt(iName)) {
375             continue;
376         }
377
378         // If characters are not equals, then it's not a match if patternChar is lowercase
379
if (patternChar < ScannerHelper.MAX_OBVIOUS) {
380             if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[patternChar] & ScannerHelper.C_UPPER_LETTER) == 0) {
381                 return false;
382             }
383         }
384         else if (Character.isJavaIdentifierPart(patternChar) && !Character.isUpperCase(patternChar)) {
385             return false;
386         }
387
388         // patternChar is uppercase, so let's find the next uppercase in name
389
while (true) {
390             if (iName == nameEnd){
391                 // We have exhausted name (and not pattern), so it's not a match
392
return false;
393             }
394
395             nameChar = name.charAt(iName);
396
397             if (nameChar < ScannerHelper.MAX_OBVIOUS) {
398                 if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[nameChar] & (ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_SPECIAL | ScannerHelper.C_DIGIT)) != 0) {
399                     // nameChar is lowercase
400
iName++;
401                 // nameChar is uppercase...
402
} else if (patternChar != nameChar) {
403                     //.. and it does not match patternChar, so it's not a match
404
return false;
405                 } else {
406                     //.. and it matched patternChar. Back to the big loop
407
break;
408                 }
409             }
410             else if (Character.isJavaIdentifierPart(nameChar) && !Character.isUpperCase(nameChar)) {
411                 // nameChar is lowercase
412
iName++;
413             // nameChar is uppercase...
414
} else if (patternChar != nameChar) {
415                 //.. and it does not match patternChar, so it's not a match
416
return false;
417             } else {
418                 //.. and it matched patternChar. Back to the big loop
419
break;
420             }
421         }
422         // At this point, either name has been exhausted, or it is at an uppercase letter.
423
// Since pattern is also at an uppercase letter
424
}
425 }
426
427 /**
428  * Returns a search pattern that combines the given two patterns into an
429  * "and" pattern. The search result will match both the left pattern and
430  * the right pattern.
431  *
432  * @param leftPattern the left pattern
433  * @param rightPattern the right pattern
434  * @return an "and" pattern
435  */

436 public static SearchPattern createAndPattern(SearchPattern leftPattern, SearchPattern rightPattern) {
437     return MatchLocator.createAndPattern(leftPattern, rightPattern);
438 }
439
440 /**
441  * Field pattern are formed by [declaringType.]name[ type]
442  * e.g. java.lang.String.serialVersionUID long
443  * field*
444  */

445 private static SearchPattern createFieldPattern(String JavaDoc patternString, int limitTo, int matchRule) {
446     
447     Scanner scanner = new Scanner(false /*comment*/, true /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3/*sourceLevel*/, null /*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/);
448     scanner.setSource(patternString.toCharArray());
449     final int InsideDeclaringPart = 1;
450     final int InsideType = 2;
451     int lastToken = -1;
452     
453     String JavaDoc declaringType = null, fieldName = null;
454     String JavaDoc type = null;
455     int mode = InsideDeclaringPart;
456     int token;
457     try {
458         token = scanner.getNextToken();
459     } catch (InvalidInputException e) {
460         return null;
461     }
462     while (token != TerminalTokens.TokenNameEOF) {
463         switch(mode) {
464             // read declaring type and fieldName
465
case InsideDeclaringPart :
466                 switch (token) {
467                     case TerminalTokens.TokenNameDOT:
468                         if (declaringType == null) {
469                             if (fieldName == null) return null;
470                             declaringType = fieldName;
471                         } else {
472                             String JavaDoc tokenSource = scanner.getCurrentTokenString();
473                             declaringType += tokenSource + fieldName;
474                         }
475                         fieldName = null;
476                         break;
477                     case TerminalTokens.TokenNameWHITESPACE:
478                         if (!(TerminalTokens.TokenNameWHITESPACE == lastToken || TerminalTokens.TokenNameDOT == lastToken))
479                             mode = InsideType;
480                         break;
481                     default: // all other tokens are considered identifiers (see bug 21763 Problem in Java search [search])
482
if (fieldName == null)
483                             fieldName = scanner.getCurrentTokenString();
484                         else
485                             fieldName += scanner.getCurrentTokenString();
486                 }
487                 break;
488             // read type
489
case InsideType:
490                 switch (token) {
491                     case TerminalTokens.TokenNameWHITESPACE:
492                         break;
493                     default: // all other tokens are considered identifiers (see bug 21763 Problem in Java search [search])
494
if (type == null)
495                             type = scanner.getCurrentTokenString();
496                         else
497                             type += scanner.getCurrentTokenString();
498                 }
499                 break;
500         }
501         lastToken = token;
502         try {
503             token = scanner.getNextToken();
504         } catch (InvalidInputException e) {
505             return null;
506         }
507     }
508     if (fieldName == null) return null;
509
510     char[] fieldNameChars = fieldName.toCharArray();
511     if (fieldNameChars.length == 1 && fieldNameChars[0] == '*') fieldNameChars = null;
512         
513     char[] declaringTypeQualification = null, declaringTypeSimpleName = null;
514     char[] typeQualification = null, typeSimpleName = null;
515
516     // extract declaring type infos
517
if (declaringType != null) {
518         char[] declaringTypePart = declaringType.toCharArray();
519         int lastDotPosition = CharOperation.lastIndexOf('.', declaringTypePart);
520         if (lastDotPosition >= 0) {
521             declaringTypeQualification = CharOperation.subarray(declaringTypePart, 0, lastDotPosition);
522             if (declaringTypeQualification.length == 1 && declaringTypeQualification[0] == '*')
523                 declaringTypeQualification = null;
524             declaringTypeSimpleName = CharOperation.subarray(declaringTypePart, lastDotPosition+1, declaringTypePart.length);
525         } else {
526             declaringTypeSimpleName = declaringTypePart;
527         }
528         if (declaringTypeSimpleName.length == 1 && declaringTypeSimpleName[0] == '*')
529             declaringTypeSimpleName = null;
530     }
531     // extract type infos
532
if (type != null) {
533         char[] typePart = type.toCharArray();
534         int lastDotPosition = CharOperation.lastIndexOf('.', typePart);
535         if (lastDotPosition >= 0) {
536             typeQualification = CharOperation.subarray(typePart, 0, lastDotPosition);
537             if (typeQualification.length == 1 && typeQualification[0] == '*') {
538                 typeQualification = null;
539             } else {
540                 // prefix with a '*' as the full qualification could be bigger (because of an import)
541
typeQualification = CharOperation.concat(IIndexConstants.ONE_STAR, typeQualification);
542             }
543             typeSimpleName = CharOperation.subarray(typePart, lastDotPosition+1, typePart.length);
544         } else {
545             typeSimpleName = typePart;
546         }
547         if (typeSimpleName.length == 1 && typeSimpleName[0] == '*')
548             typeSimpleName = null;
549     }
550     // Create field pattern
551
boolean findDeclarations = false;
552     boolean readAccess = false;
553     boolean writeAccess = false;
554     switch (limitTo) {
555         case IJavaSearchConstants.DECLARATIONS :
556             findDeclarations = true;
557             break;
558         case IJavaSearchConstants.REFERENCES :
559             readAccess = true;
560             writeAccess = true;
561             break;
562         case IJavaSearchConstants.READ_ACCESSES :
563             readAccess = true;
564             break;
565         case IJavaSearchConstants.WRITE_ACCESSES :
566             writeAccess = true;
567             break;
568         case IJavaSearchConstants.ALL_OCCURRENCES :
569             findDeclarations = true;
570             readAccess = true;
571             writeAccess = true;
572             break;
573     }
574     return new FieldPattern(
575             findDeclarations,
576             readAccess,
577             writeAccess,
578             fieldNameChars,
579             declaringTypeQualification,
580             declaringTypeSimpleName,
581             typeQualification,
582             typeSimpleName,
583             matchRule);
584 }
585
586 /**
587  * Method pattern are formed by:<br>
588  * [declaringType '.'] ['&lt;' typeArguments '&gt;'] selector ['(' parameterTypes ')'] [returnType]
589  * <br>e.g.<ul>
590  * <li>java.lang.Runnable.run() void</li>
591  * <li>main(*)</li>
592  * <li>&lt;String&gt;toArray(String[])</li>
593  * </ul>
594  * Constructor pattern are formed by:<br>
595  * [declaringQualification '.'] ['&lt;' typeArguments '&gt;'] type ['(' parameterTypes ')']
596  * <br>e.g.<ul>
597  * <li>java.lang.Object()</li>
598  * <li>Main(*)</li>
599  * <li>&lt;Exception&gt;Sample(Exception)</li>
600  * </ul>
601  * Type arguments have the same pattern that for type patterns
602  * @see #createTypePattern(String,int,int,char)
603  */

604 private static SearchPattern createMethodOrConstructorPattern(String JavaDoc patternString, int limitTo, int matchRule, boolean isConstructor) {
605     
606     Scanner scanner = new Scanner(false /*comment*/, true /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3/*sourceLevel*/, null /*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/);
607     scanner.setSource(patternString.toCharArray());
608     final int InsideSelector = 1;
609     final int InsideTypeArguments = 2;
610     final int InsideParameter = 3;
611     final int InsideReturnType = 4;
612     int lastToken = -1;
613     
614     String JavaDoc declaringType = null, selector = null, parameterType = null;
615     String JavaDoc[] parameterTypes = null;
616     char[][] typeArguments = null;
617     String JavaDoc typeArgumentsString = null;
618     int parameterCount = -1;
619     String JavaDoc returnType = null;
620     boolean foundClosingParenthesis = false;
621     int mode = InsideSelector;
622     int token, argCount = 0;
623     try {
624         token = scanner.getNextToken();
625     } catch (InvalidInputException e) {
626         return null;
627     }
628     while (token != TerminalTokens.TokenNameEOF) {
629         switch(mode) {
630             // read declaring type and selector
631
case InsideSelector :
632                 if (argCount == 0) {
633                     switch (token) {
634                         case TerminalTokens.TokenNameLESS:
635                             argCount++;
636                             if (selector == null || lastToken == TerminalTokens.TokenNameDOT) {
637                                 if (typeArgumentsString != null) return null; // invalid syntax
638
typeArgumentsString = scanner.getCurrentTokenString();
639                                 mode = InsideTypeArguments;
640                                 break;
641                             }
642                             if (declaringType == null) {
643                                 declaringType = selector;
644                             } else {
645                                 declaringType += '.' + selector;
646                             }
647                             declaringType += scanner.getCurrentTokenString();
648                             selector = null;
649                             break;
650                         case TerminalTokens.TokenNameDOT:
651                             if (typeArgumentsString != null) return null; // invalid syntax
652
if (declaringType == null) {
653                                 if (selector == null) return null; // invalid syntax
654
declaringType = selector;
655                             } else if (selector != null) {
656                                 declaringType += scanner.getCurrentTokenString() + selector;
657                             }
658                             selector = null;
659                             break;
660                         case TerminalTokens.TokenNameLPAREN:
661                             parameterTypes = new String JavaDoc[5];
662                             parameterCount = 0;
663                             mode = InsideParameter;
664                             break;
665                         case TerminalTokens.TokenNameWHITESPACE:
666                             switch (lastToken) {
667                                 case TerminalTokens.TokenNameWHITESPACE:
668                                 case TerminalTokens.TokenNameDOT:
669                                 case TerminalTokens.TokenNameGREATER:
670                                 case TerminalTokens.TokenNameRIGHT_SHIFT:
671                                 case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT:
672                                     break;
673                                 default:
674                                     mode = InsideReturnType;
675                                     break;
676                             }
677                             break;
678                         default: // all other tokens are considered identifiers (see bug 21763 Problem in Java search [search])
679
if (selector == null)
680                                 selector = scanner.getCurrentTokenString();
681                             else
682                                 selector += scanner.getCurrentTokenString();
683                             break;
684                     }
685                 } else {
686                     if (declaringType == null) return null; // invalid syntax
687
switch (token) {
688                         case TerminalTokens.TokenNameGREATER:
689                         case TerminalTokens.TokenNameRIGHT_SHIFT:
690                         case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT:
691                             argCount--;
692                             break;
693                         case TerminalTokens.TokenNameLESS:
694                             argCount++;
695                             break;
696                     }
697                     declaringType += scanner.getCurrentTokenString();
698                 }
699                 break;
700             // read type arguments
701
case InsideTypeArguments:
702                 if (typeArgumentsString == null) return null; // invalid syntax
703
typeArgumentsString += scanner.getCurrentTokenString();
704                 switch (token) {
705                     case TerminalTokens.TokenNameGREATER:
706                     case TerminalTokens.TokenNameRIGHT_SHIFT:
707                     case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT:
708                         argCount--;
709                         if (argCount == 0) {
710                             String JavaDoc pseudoType = "Type"+typeArgumentsString; //$NON-NLS-1$
711
typeArguments = Signature.getTypeArguments(Signature.createTypeSignature(pseudoType, false).toCharArray());
712                             mode = InsideSelector;
713                         }
714                         break;
715                     case TerminalTokens.TokenNameLESS:
716                         argCount++;
717                         break;
718                 }
719                 break;
720             // read parameter types
721
case InsideParameter :
722                 if (argCount == 0) {
723                     switch (token) {
724                         case TerminalTokens.TokenNameWHITESPACE:
725                             break;
726                         case TerminalTokens.TokenNameCOMMA:
727                             if (parameterType == null) return null;
728                             if (parameterTypes != null) {
729                                 if (parameterTypes.length == parameterCount)
730                                     System.arraycopy(parameterTypes, 0, parameterTypes = new String JavaDoc[parameterCount*2], 0, parameterCount);
731                                 parameterTypes[parameterCount++] = parameterType;
732                             }
733                             parameterType = null;
734                             break;
735                         case TerminalTokens.TokenNameRPAREN:
736                             foundClosingParenthesis = true;
737                             if (parameterType != null && parameterTypes != null) {
738                                 if (parameterTypes.length == parameterCount)
739                                     System.arraycopy(parameterTypes, 0, parameterTypes = new String JavaDoc[parameterCount*2], 0, parameterCount);
740                                 parameterTypes[parameterCount++] = parameterType;
741                             }
742                             mode = isConstructor ? InsideTypeArguments : InsideReturnType;
743                             break;
744                         case TerminalTokens.TokenNameLESS:
745                             argCount++;
746                             if (parameterType == null) return null; // invalid syntax
747
// fall through next case to add token
748
default: // all other tokens are considered identifiers (see bug 21763 Problem in Java search [search])
749
if (parameterType == null)
750                                 parameterType = scanner.getCurrentTokenString();
751                             else
752                                 parameterType += scanner.getCurrentTokenString();
753                     }
754                 } else {
755                     if (parameterType == null) return null; // invalid syntax
756
switch (token) {
757                         case TerminalTokens.TokenNameGREATER:
758                         case TerminalTokens.TokenNameRIGHT_SHIFT:
759                         case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT:
760                             argCount--;
761                             break;
762                         case TerminalTokens.TokenNameLESS:
763                             argCount++;
764                             break;
765                     }
766                     parameterType += scanner.getCurrentTokenString();
767                 }
768                 break;
769             // read return type
770
case InsideReturnType:
771                 if (argCount == 0) {
772                     switch (token) {
773                         case TerminalTokens.TokenNameWHITESPACE:
774                             break;
775                         case TerminalTokens.TokenNameLPAREN:
776                             parameterTypes = new String JavaDoc[5];
777                             parameterCount = 0;
778                             mode = InsideParameter;
779                             break;
780                         case TerminalTokens.TokenNameLESS:
781                             argCount++;
782                             if (returnType == null) return null; // invalid syntax
783
// fall through next case to add token
784
default: // all other tokens are considered identifiers (see bug 21763 Problem in Java search [search])
785
if (returnType == null)
786                                 returnType = scanner.getCurrentTokenString();
787                             else
788                                 returnType += scanner.getCurrentTokenString();
789                     }
790                 } else {
791                     if (returnType == null) return null; // invalid syntax
792
switch (token) {
793                         case TerminalTokens.TokenNameGREATER:
794                         case TerminalTokens.TokenNameRIGHT_SHIFT:
795                         case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT:
796                             argCount--;
797                             break;
798                         case TerminalTokens.TokenNameLESS:
799                             argCount++;
800                             break;
801                     }
802                     returnType += scanner.getCurrentTokenString();
803                 }
804                 break;
805         }
806         lastToken = token;
807         try {
808             token = scanner.getNextToken();
809         } catch (InvalidInputException e) {
810             return null;
811         }
812     }
813     // parenthesis mismatch
814
if (parameterCount>0 && !foundClosingParenthesis) return null;
815     // type arguments mismatch
816
if (argCount > 0) return null;
817
818     char[] selectorChars = null;
819     if (isConstructor) {
820         // retrieve type for constructor patterns
821
if (declaringType == null)
822             declaringType = selector;
823         else if (selector != null)
824             declaringType += '.' + selector;
825     } else {
826         // get selector chars
827
if (selector == null) return null;
828         selectorChars = selector.toCharArray();
829         if (selectorChars.length == 1 && selectorChars[0] == '*')
830             selectorChars = null;
831     }
832         
833     char[] declaringTypeQualification = null, declaringTypeSimpleName = null;
834     char[] returnTypeQualification = null, returnTypeSimpleName = null;
835     char[][] parameterTypeQualifications = null, parameterTypeSimpleNames = null;
836     // Signatures
837
String JavaDoc declaringTypeSignature = null;
838     String JavaDoc returnTypeSignature = null;
839     String JavaDoc[] parameterTypeSignatures = null;
840
841     // extract declaring type infos
842
if (declaringType != null) {
843         // get declaring type part and signature
844
char[] declaringTypePart = null;
845         try {
846             declaringTypeSignature = Signature.createTypeSignature(declaringType, false);
847             if (declaringTypeSignature.indexOf(Signature.C_GENERIC_START) < 0) {
848                 declaringTypePart = declaringType.toCharArray();
849             } else {
850                 declaringTypePart = Signature.toCharArray(Signature.getTypeErasure(declaringTypeSignature.toCharArray()));
851             }
852         }
853         catch (IllegalArgumentException JavaDoc iae) {
854             // declaring type is invalid
855
return null;
856         }
857         int lastDotPosition = CharOperation.lastIndexOf('.', declaringTypePart);
858         if (lastDotPosition >= 0) {
859             declaringTypeQualification = CharOperation.subarray(declaringTypePart, 0, lastDotPosition);
860             if (declaringTypeQualification.length == 1 && declaringTypeQualification[0] == '*')
861                 declaringTypeQualification = null;
862             declaringTypeSimpleName = CharOperation.subarray(declaringTypePart, lastDotPosition+1, declaringTypePart.length);
863         } else {
864             declaringTypeSimpleName = declaringTypePart;
865         }
866         if (declaringTypeSimpleName.length == 1 && declaringTypeSimpleName[0] == '*')
867             declaringTypeSimpleName = null;
868     }
869     // extract parameter types infos
870
if (parameterCount >= 0) {
871         parameterTypeQualifications = new char[parameterCount][];
872         parameterTypeSimpleNames = new char[parameterCount][];
873         parameterTypeSignatures = new String JavaDoc[parameterCount];
874         for (int i = 0; i < parameterCount; i++) {
875             // get parameter type part and signature
876
char[] parameterTypePart = null;
877             try {
878                 if (parameterTypes != null) {
879                     parameterTypeSignatures[i] = Signature.createTypeSignature(parameterTypes[i], false);
880                     if (parameterTypeSignatures[i].indexOf(Signature.C_GENERIC_START) < 0) {
881                         parameterTypePart = parameterTypes[i].toCharArray();
882                     } else {
883                         parameterTypePart = Signature.toCharArray(Signature.getTypeErasure(parameterTypeSignatures[i].toCharArray()));
884                     }
885                 }
886             }
887             catch (IllegalArgumentException JavaDoc iae) {
888                 // string is not a valid type syntax
889
return null;
890             }
891             int lastDotPosition = parameterTypePart==null ? -1 : CharOperation.lastIndexOf('.', parameterTypePart);
892             if (parameterTypePart != null && lastDotPosition >= 0) {
893                 parameterTypeQualifications[i] = CharOperation.subarray(parameterTypePart, 0, lastDotPosition);
894                 if (parameterTypeQualifications[i].length == 1 && parameterTypeQualifications[i][0] == '*') {
895                     parameterTypeQualifications[i] = null;
896                 } else {
897                     // prefix with a '*' as the full qualification could be bigger (because of an import)
898
parameterTypeQualifications[i] = CharOperation.concat(IIndexConstants.ONE_STAR, parameterTypeQualifications[i]);
899                 }
900                 parameterTypeSimpleNames[i] = CharOperation.subarray(parameterTypePart, lastDotPosition+1, parameterTypePart.length);
901             } else {
902                 parameterTypeQualifications[i] = null;
903                 parameterTypeSimpleNames[i] = parameterTypePart;
904             }
905             if (parameterTypeSimpleNames[i].length == 1 && parameterTypeSimpleNames[i][0] == '*')
906                 parameterTypeSimpleNames[i] = null;
907         }
908     }
909     // extract return type infos
910
if (returnType != null) {
911         // get return type part and signature
912
char[] returnTypePart = null;
913         try {
914             returnTypeSignature = Signature.createTypeSignature(returnType, false);
915             if (returnTypeSignature.indexOf(Signature.C_GENERIC_START) < 0) {
916                 returnTypePart = returnType.toCharArray();
917             } else {
918                 returnTypePart = Signature.toCharArray(Signature.getTypeErasure(returnTypeSignature.toCharArray()));
919             }
920         }
921         catch (IllegalArgumentException JavaDoc iae) {
922             // declaring type is invalid
923
return null;
924         }
925         int lastDotPosition = CharOperation.lastIndexOf('.', returnTypePart);
926         if (lastDotPosition >= 0) {
927             returnTypeQualification = CharOperation.subarray(returnTypePart, 0, lastDotPosition);
928             if (returnTypeQualification.length == 1 && returnTypeQualification[0] == '*') {
929                 returnTypeQualification = null;
930             } else {
931                 // because of an import
932
returnTypeQualification = CharOperation.concat(IIndexConstants.ONE_STAR, returnTypeQualification);
933             }
934             returnTypeSimpleName = CharOperation.subarray(returnTypePart, lastDotPosition+1, returnTypePart.length);
935         } else {
936             returnTypeSimpleName = returnTypePart;
937         }
938         if (returnTypeSimpleName.length == 1 && returnTypeSimpleName[0] == '*')
939             returnTypeSimpleName = null;
940     }
941     // Create method/constructor pattern
942
boolean findDeclarations = true;
943     boolean findReferences = true;
944     switch (limitTo) {
945         case IJavaSearchConstants.DECLARATIONS :
946             findReferences = false;
947             break;
948         case IJavaSearchConstants.REFERENCES :
949             findDeclarations = false;
950             break;
951         case IJavaSearchConstants.ALL_OCCURRENCES :
952             break;
953     }
954     if (isConstructor) {
955         return new ConstructorPattern(
956                 findDeclarations,
957                 findReferences,
958                 declaringTypeSimpleName,
959                 declaringTypeQualification,
960                 declaringTypeSignature,
961                 parameterTypeQualifications,
962                 parameterTypeSimpleNames,
963                 parameterTypeSignatures,
964                 typeArguments,
965                 matchRule);
966     } else {
967         return new MethodPattern(
968                 findDeclarations,
969                 findReferences,
970                 selectorChars,
971                 declaringTypeQualification,
972                 declaringTypeSimpleName,
973                 declaringTypeSignature,
974                 returnTypeQualification,
975                 returnTypeSimpleName,
976                 returnTypeSignature,
977                 parameterTypeQualifications,
978                 parameterTypeSimpleNames,
979                 parameterTypeSignatures,
980                 typeArguments,
981                 matchRule);
982     }
983 }
984
985 /**
986  * Returns a search pattern that combines the given two patterns into an
987  * "or" pattern. The search result will match either the left pattern or the
988  * right pattern.
989  *
990  * @param leftPattern the left pattern
991  * @param rightPattern the right pattern
992  * @return an "or" pattern
993  */

994 public static SearchPattern createOrPattern(SearchPattern leftPattern, SearchPattern rightPattern) {
995     return new OrPattern(leftPattern, rightPattern);
996 }
997
998 private static SearchPattern createPackagePattern(String JavaDoc patternString, int limitTo, int matchRule) {
999     switch (limitTo) {
1000        case IJavaSearchConstants.DECLARATIONS :
1001            return new PackageDeclarationPattern(patternString.toCharArray(), matchRule);
1002        case IJavaSearchConstants.REFERENCES :
1003            return new PackageReferencePattern(patternString.toCharArray(), matchRule);
1004        case IJavaSearchConstants.ALL_OCCURRENCES :
1005            return new OrPattern(
1006                new PackageDeclarationPattern(patternString.toCharArray(), matchRule),
1007                new PackageReferencePattern(patternString.toCharArray(), matchRule)
1008            );
1009    }
1010    return null;
1011}
1012
1013/**
1014 * Returns a search pattern based on a given string pattern. The string patterns support '*' wild-cards.
1015 * The remaining parameters are used to narrow down the type of expected results.
1016 *
1017 * <br>
1018 * Examples:
1019 * <ul>
1020 * <li>search for case insensitive references to <code>Object</code>:
1021 * <code>createSearchPattern("Object", TYPE, REFERENCES, false);</code></li>
1022 * <li>search for case sensitive references to exact <code>Object()</code> constructor:
1023 * <code>createSearchPattern("java.lang.Object()", CONSTRUCTOR, REFERENCES, true);</code></li>
1024 * <li>search for implementers of <code>java.lang.Runnable</code>:
1025 * <code>createSearchPattern("java.lang.Runnable", TYPE, IMPLEMENTORS, true);</code></li>
1026 * </ul>
1027 * @param stringPattern the given pattern
1028 * @param searchFor determines the nature of the searched elements
1029 * <ul>
1030 * <li>{@link IJavaSearchConstants#CLASS}: only look for classes</li>
1031 * <li>{@link IJavaSearchConstants#INTERFACE}: only look for interfaces</li>
1032 * <li>{@link IJavaSearchConstants#ENUM}: only look for enumeration</li>
1033 * <li>{@link IJavaSearchConstants#ANNOTATION_TYPE}: only look for annotation type</li>
1034 * <li>{@link IJavaSearchConstants#CLASS_AND_ENUM}: only look for classes and enumerations</li>
1035 * <li>{@link IJavaSearchConstants#CLASS_AND_INTERFACE}: only look for classes and interfaces</li>
1036 * <li>{@link IJavaSearchConstants#TYPE}: look for all types (ie. classes, interfaces, enum and annotation types)</li>
1037 * <li>{@link IJavaSearchConstants#FIELD}: look for fields</li>
1038 * <li>{@link IJavaSearchConstants#METHOD}: look for methods</li>
1039 * <li>{@link IJavaSearchConstants#CONSTRUCTOR}: look for constructors</li>
1040 * <li>{@link IJavaSearchConstants#PACKAGE}: look for packages</li>
1041 * </ul>
1042 * @param limitTo determines the nature of the expected matches
1043 * <ul>
1044 * <li>{@link IJavaSearchConstants#DECLARATIONS}: will search declarations matching
1045 * with the corresponding element. In case the element is a method, declarations of matching
1046 * methods in subtypes will also be found, allowing to find declarations of abstract methods, etc.<br>
1047 * Note that additional flags {@link IJavaSearchConstants#IGNORE_DECLARING_TYPE} and
1048 * {@link IJavaSearchConstants#IGNORE_RETURN_TYPE} are ignored for string patterns.
1049 * This is due to the fact that client may omit to define them in string pattern to have same behavior.
1050 * </li>
1051 * <li>{@link IJavaSearchConstants#REFERENCES}: will search references to the given element.</li>
1052 * <li>{@link IJavaSearchConstants#ALL_OCCURRENCES}: will search for either declarations or
1053 * references as specified above.
1054 * </li>
1055 * <li>{@link IJavaSearchConstants#IMPLEMENTORS}: for types, will find all types
1056 * which directly implement/extend a given interface.
1057 * Note that types may be only classes or only interfaces if {@link IJavaSearchConstants#CLASS } or
1058 * {@link IJavaSearchConstants#INTERFACE} is respectively used instead of {@link IJavaSearchConstants#TYPE}.
1059 * </li>
1060 * </ul>
1061 * @param matchRule one of {@link #R_EXACT_MATCH}, {@link #R_PREFIX_MATCH}, {@link #R_PATTERN_MATCH},
1062 * {@link #R_REGEXP_MATCH}, {@link #R_CAMELCASE_MATCH} combined with one of following values:
1063 * {@link #R_CASE_SENSITIVE}, {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH}.
1064 * e.g. {@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE} if an exact and case sensitive match is requested,
1065 * {@link #R_PREFIX_MATCH} if a prefix non case sensitive match is requested or {@link #R_EXACT_MATCH} | {@link #R_ERASURE_MATCH}
1066 * if a non case sensitive and erasure match is requested.<br>
1067 * Note that {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH} have no effect
1068 * on non-generic types/methods search.<br>
1069 * Note also that default behavior for generic types/methods search is to find exact matches.
1070 * @return a search pattern on the given string pattern, or <code>null</code> if the string pattern is ill-formed
1071 */

1072public static SearchPattern createPattern(String JavaDoc stringPattern, int searchFor, int limitTo, int matchRule) {
1073    if (stringPattern == null || stringPattern.length() == 0) return null;
1074
1075    if ((matchRule = validateMatchRule(stringPattern, matchRule)) == -1) {
1076        return null;
1077    }
1078
1079    // Ignore additional nature flags
1080
limitTo &= ~(IJavaSearchConstants.IGNORE_DECLARING_TYPE+IJavaSearchConstants.IGNORE_RETURN_TYPE);
1081
1082    switch (searchFor) {
1083        case IJavaSearchConstants.CLASS:
1084            return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.CLASS_SUFFIX);
1085        case IJavaSearchConstants.CLASS_AND_INTERFACE:
1086            return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.CLASS_AND_INTERFACE_SUFFIX);
1087        case IJavaSearchConstants.CLASS_AND_ENUM:
1088            return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.CLASS_AND_ENUM_SUFFIX);
1089        case IJavaSearchConstants.INTERFACE:
1090            return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.INTERFACE_SUFFIX);
1091        case IJavaSearchConstants.INTERFACE_AND_ANNOTATION:
1092            return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.INTERFACE_AND_ANNOTATION_SUFFIX);
1093        case IJavaSearchConstants.ENUM:
1094            return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.ENUM_SUFFIX);
1095        case IJavaSearchConstants.ANNOTATION_TYPE:
1096            return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.ANNOTATION_TYPE_SUFFIX);
1097        case IJavaSearchConstants.TYPE:
1098            return createTypePattern(stringPattern, limitTo, matchRule, IIndexConstants.TYPE_SUFFIX);
1099        case IJavaSearchConstants.METHOD:
1100            return createMethodOrConstructorPattern(stringPattern, limitTo, matchRule, false/*not a constructor*/);
1101        case IJavaSearchConstants.CONSTRUCTOR:
1102            return createMethodOrConstructorPattern(stringPattern, limitTo, matchRule, true/*constructor*/);
1103        case IJavaSearchConstants.FIELD:
1104            return createFieldPattern(stringPattern, limitTo, matchRule);
1105        case IJavaSearchConstants.PACKAGE:
1106            return createPackagePattern(stringPattern, limitTo, matchRule);
1107    }
1108    return null;
1109}
1110
1111/**
1112 * Returns a search pattern based on a given Java element.
1113 * The pattern is used to trigger the appropriate search.
1114 * <br>
1115 * Note that for generic searches, the returned pattern consider {@link #R_ERASURE_MATCH} matches.
1116 * If other kind of generic matches (ie. {@link #R_EXACT_MATCH} or {@link #R_EQUIVALENT_MATCH})
1117 * are expected, {@link #createPattern(IJavaElement, int, int)} method need to be used instead with
1118 * the explicit match rule specified.
1119 * <br>
1120 * The pattern can be parameterized as follows:
1121 *
1122 * @param element the Java element the search pattern is based on
1123 * @param limitTo determines the nature of the expected matches
1124 * <ul>
1125 * <li>{@link IJavaSearchConstants#DECLARATIONS}: will search declarations matching
1126 * with the corresponding element. In case the element is a method, declarations of matching
1127 * methods in subtypes will also be found, allowing to find declarations of abstract methods, etc.
1128 * Some additional flags may be specified while searching declaration:
1129 * <ul>
1130 * <li>{@link IJavaSearchConstants#IGNORE_DECLARING_TYPE}: declaring type will be ignored
1131 * during the search.<br>
1132 * For example using following test case:
1133 * <pre>
1134 * class A { A method() { return null; } }
1135 * class B extends A { B method() { return null; } }
1136 * class C { A method() { return null; } }
1137 * </pre>
1138 * search for <code>method</code> declaration with this flag
1139 * will return 2 matches: in A and in C
1140 * </li>
1141 * <li>{@link IJavaSearchConstants#IGNORE_RETURN_TYPE}: return type will be ignored
1142 * during the search.<br>
1143 * Using same example, search for <code>method</code> declaration with this flag
1144 * will return 2 matches: in A and in B.
1145 * </li>
1146 * </ul>
1147 * Note that these two flags may be combined and both declaring and return types can be ignored
1148 * during the search. Then, using same example, search for <code>method</code> declaration
1149 * with these 2 flags will return 3 matches: in A, in B and in C
1150 * </li>
1151 * <li>{@link IJavaSearchConstants#REFERENCES}: will search references to the given element.</li>
1152 * <li>{@link IJavaSearchConstants#ALL_OCCURRENCES}: will search for either declarations or
1153 * references as specified above.
1154 * </li>
1155 * <li>{@link IJavaSearchConstants#IMPLEMENTORS}: for types, will find all types
1156 * which directly implement/extend a given interface.
1157 * </li>
1158 * </ul>
1159 * @return a search pattern for a Java element or <code>null</code> if the given element is ill-formed
1160 */

1161public static SearchPattern createPattern(IJavaElement element, int limitTo) {
1162    return createPattern(element, limitTo, R_EXACT_MATCH | R_CASE_SENSITIVE | R_ERASURE_MATCH);
1163}
1164
1165/**
1166 * Returns a search pattern based on a given Java element.
1167 * The pattern is used to trigger the appropriate search, and can be parameterized as follows:
1168 *
1169 * @param element the Java element the search pattern is based on
1170 * @param limitTo determines the nature of the expected matches
1171 * <ul>
1172 * <li>{@link IJavaSearchConstants#DECLARATIONS}: will search declarations matching
1173 * with the corresponding element. In case the element is a method, declarations of matching
1174 * methods in subtypes will also be found, allowing to find declarations of abstract methods, etc.
1175 * Some additional flags may be specified while searching declaration:
1176 * <ul>
1177 * <li>{@link IJavaSearchConstants#IGNORE_DECLARING_TYPE}: declaring type will be ignored
1178 * during the search.<br>
1179 * For example using following test case:
1180 * <pre>
1181 * class A { A method() { return null; } }
1182 * class B extends A { B method() { return null; } }
1183 * class C { A method() { return null; } }
1184 * </pre>
1185 * search for <code>method</code> declaration with this flag
1186 * will return 2 matches: in A and in C
1187 * </li>
1188 * <li>{@link IJavaSearchConstants#IGNORE_RETURN_TYPE}: return type will be ignored
1189 * during the search.<br>
1190 * Using same example, search for <code>method</code> declaration with this flag
1191 * will return 2 matches: in A and in B.
1192 * </li>
1193 * </ul>
1194 * Note that these two flags may be combined and both declaring and return types can be ignored
1195 * during the search. Then, using same example, search for <code>method</code> declaration
1196 * with these 2 flags will return 3 matches: in A, in B and in C
1197 * </li>
1198 * <li>{@link IJavaSearchConstants#REFERENCES}: will search references to the given element.</li>
1199 * <li>{@link IJavaSearchConstants#ALL_OCCURRENCES}: will search for either declarations or
1200 * references as specified above.
1201 * </li>
1202 * <li>{@link IJavaSearchConstants#IMPLEMENTORS}: for types, will find all types
1203 * which directly implement/extend a given interface.
1204 * </li>
1205 * </ul>
1206 * @param matchRule one of {@link #R_EXACT_MATCH}, {@link #R_PREFIX_MATCH}, {@link #R_PATTERN_MATCH},
1207 * {@link #R_REGEXP_MATCH}, {@link #R_CAMELCASE_MATCH} combined with one of following values:
1208 * {@link #R_CASE_SENSITIVE}, {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH}.
1209 * e.g. {@link #R_EXACT_MATCH} | {@link #R_CASE_SENSITIVE} if an exact and case sensitive match is requested,
1210 * {@link #R_PREFIX_MATCH} if a prefix non case sensitive match is requested or {@link #R_EXACT_MATCH} |{@link #R_ERASURE_MATCH}
1211 * if a non case sensitive and erasure match is requested.<br>
1212 * Note that {@link #R_ERASURE_MATCH} or {@link #R_EQUIVALENT_MATCH} have no effect on non-generic types
1213 * or methods search.<br>
1214 * Note also that default behavior for generic types or methods is to find exact matches.
1215 * @return a search pattern for a Java element or <code>null</code> if the given element is ill-formed
1216 * @since 3.1
1217 */

1218public static SearchPattern createPattern(IJavaElement element, int limitTo, int matchRule) {
1219    SearchPattern searchPattern = null;
1220    int lastDot;
1221    boolean ignoreDeclaringType = false;
1222    boolean ignoreReturnType = false;
1223    int maskedLimitTo = limitTo & ~(IJavaSearchConstants.IGNORE_DECLARING_TYPE+IJavaSearchConstants.IGNORE_RETURN_TYPE);
1224    if (maskedLimitTo == IJavaSearchConstants.DECLARATIONS || maskedLimitTo == IJavaSearchConstants.ALL_OCCURRENCES) {
1225        ignoreDeclaringType = (limitTo & IJavaSearchConstants.IGNORE_DECLARING_TYPE) != 0;
1226        ignoreReturnType = (limitTo & IJavaSearchConstants.IGNORE_RETURN_TYPE) != 0;
1227    }
1228    char[] declaringSimpleName = null;
1229    char[] declaringQualification = null;
1230    switch (element.getElementType()) {
1231        case IJavaElement.FIELD :
1232            IField field = (IField) element;
1233            if (!ignoreDeclaringType) {
1234                IType declaringClass = field.getDeclaringType();
1235                declaringSimpleName = declaringClass.getElementName().toCharArray();
1236                declaringQualification = declaringClass.getPackageFragment().getElementName().toCharArray();
1237                char[][] enclosingNames = enclosingTypeNames(declaringClass);
1238                if (enclosingNames.length > 0) {
1239                    declaringQualification = CharOperation.concat(declaringQualification, CharOperation.concatWith(enclosingNames, '.'), '.');
1240                }
1241            }
1242            char[] name = field.getElementName().toCharArray();
1243            char[] typeSimpleName = null;
1244            char[] typeQualification = null;
1245            String JavaDoc typeSignature = null;
1246            if (!ignoreReturnType) {
1247                try {
1248                    typeSignature = field.getTypeSignature();
1249                    char[] signature = typeSignature.toCharArray();
1250                    char[] typeErasure = Signature.toCharArray(Signature.getTypeErasure(signature));
1251                    CharOperation.replace(typeErasure, '$', '.');
1252                    if ((lastDot = CharOperation.lastIndexOf('.', typeErasure)) == -1) {
1253                        typeSimpleName = typeErasure;
1254                    } else {
1255                        typeSimpleName = CharOperation.subarray(typeErasure, lastDot + 1, typeErasure.length);
1256                        typeQualification = CharOperation.subarray(typeErasure, 0, lastDot);
1257                        if (!field.isBinary()) {
1258                            // prefix with a '*' as the full qualification could be bigger (because of an import)
1259
typeQualification = CharOperation.concat(IIndexConstants.ONE_STAR, typeQualification);
1260                        }
1261                    }
1262                } catch (JavaModelException e) {
1263                    return null;
1264                }
1265            }
1266            // Create field pattern
1267
boolean findDeclarations = false;
1268            boolean readAccess = false;
1269            boolean writeAccess = false;
1270            switch (maskedLimitTo) {
1271                case IJavaSearchConstants.DECLARATIONS :
1272                    findDeclarations = true;
1273                    break;
1274                case IJavaSearchConstants.REFERENCES :
1275                    readAccess = true;
1276                    writeAccess = true;
1277                    break;
1278                case IJavaSearchConstants.READ_ACCESSES :
1279                    readAccess = true;
1280                    break;
1281                case IJavaSearchConstants.WRITE_ACCESSES :
1282                    writeAccess = true;
1283                    break;
1284                case IJavaSearchConstants.ALL_OCCURRENCES :
1285                    findDeclarations = true;
1286                    readAccess = true;
1287                    writeAccess = true;
1288                    break;
1289            }
1290            searchPattern =
1291                new FieldPattern(
1292                    findDeclarations,
1293                    readAccess,
1294                    writeAccess,
1295                    name,
1296                    declaringQualification,
1297                    declaringSimpleName,
1298                    typeQualification,
1299                    typeSimpleName,
1300                    typeSignature,
1301                    matchRule);
1302            break;
1303        case IJavaElement.IMPORT_DECLARATION :
1304            String JavaDoc elementName = element.getElementName();
1305            lastDot = elementName.lastIndexOf('.');
1306            if (lastDot == -1) return null; // invalid import declaration
1307
IImportDeclaration importDecl = (IImportDeclaration)element;
1308            if (importDecl.isOnDemand()) {
1309                searchPattern = createPackagePattern(elementName.substring(0, lastDot), maskedLimitTo, matchRule);
1310            } else {
1311                searchPattern =
1312                    createTypePattern(
1313                        elementName.substring(lastDot+1).toCharArray(),
1314                        elementName.substring(0, lastDot).toCharArray(),
1315                        null,
1316                        null,
1317                        null,
1318                        maskedLimitTo,
1319                        matchRule);
1320            }
1321            break;
1322        case IJavaElement.LOCAL_VARIABLE :
1323            LocalVariable localVar = (LocalVariable) element;
1324            boolean findVarDeclarations = false;
1325            boolean findVarReadAccess = false;
1326            boolean findVarWriteAccess = false;
1327            switch (maskedLimitTo) {
1328                case IJavaSearchConstants.DECLARATIONS :
1329                    findVarDeclarations = true;
1330                    break;
1331                case IJavaSearchConstants.REFERENCES :
1332                    findVarReadAccess = true;
1333                    findVarWriteAccess = true;
1334                    break;
1335                case IJavaSearchConstants.READ_ACCESSES :
1336                    findVarReadAccess = true;
1337                    break;
1338                case IJavaSearchConstants.WRITE_ACCESSES :
1339                    findVarWriteAccess = true;
1340                    break;
1341                case IJavaSearchConstants.ALL_OCCURRENCES :
1342                    findVarDeclarations = true;
1343                    findVarReadAccess = true;
1344                    findVarWriteAccess = true;
1345                    break;
1346            }
1347            searchPattern =
1348                new LocalVariablePattern(
1349                    findVarDeclarations,
1350                    findVarReadAccess,
1351                    findVarWriteAccess,
1352                    localVar,
1353                    matchRule);
1354            break;
1355        case IJavaElement.TYPE_PARAMETER:
1356            ITypeParameter typeParam = (ITypeParameter) element;
1357            boolean findParamDeclarations = true;
1358            boolean findParamReferences = true;
1359            switch (maskedLimitTo) {
1360                case IJavaSearchConstants.DECLARATIONS :
1361                    findParamReferences = false;
1362                    break;
1363                case IJavaSearchConstants.REFERENCES :
1364                    findParamDeclarations = false;
1365                    break;
1366            }
1367            searchPattern =
1368                new TypeParameterPattern(
1369                    findParamDeclarations,
1370                    findParamReferences,
1371                    typeParam,
1372                    matchRule);
1373            break;
1374        case IJavaElement.METHOD :
1375            IMethod method = (IMethod) element;
1376            boolean isConstructor;
1377            try {
1378                isConstructor = method.isConstructor();
1379            } catch (JavaModelException e) {
1380                return null;
1381            }
1382            IType declaringClass = method.getDeclaringType();
1383            if (ignoreDeclaringType) {
1384                if (isConstructor) declaringSimpleName = declaringClass.getElementName().toCharArray();
1385            } else {
1386                declaringSimpleName = declaringClass.getElementName().toCharArray();
1387                declaringQualification = declaringClass.getPackageFragment().getElementName().toCharArray();
1388                char[][] enclosingNames = enclosingTypeNames(declaringClass);
1389                if (enclosingNames.length > 0) {
1390                    declaringQualification = CharOperation.concat(declaringQualification, CharOperation.concatWith(enclosingNames, '.'), '.');
1391                }
1392            }
1393            char[] selector = method.getElementName().toCharArray();
1394            char[] returnSimpleName = null;
1395            char[] returnQualification = null;
1396            String JavaDoc returnSignature = null;
1397            if (!ignoreReturnType) {
1398                try {
1399                    returnSignature = method.getReturnType();
1400                    char[] signature = returnSignature.toCharArray();
1401                    char[] returnErasure = Signature.toCharArray(Signature.getTypeErasure(signature));
1402                    CharOperation.replace(returnErasure, '$', '.');
1403                    if ((lastDot = CharOperation.lastIndexOf('.', returnErasure)) == -1) {
1404                        returnSimpleName = returnErasure;
1405                    } else {
1406                        returnSimpleName = CharOperation.subarray(returnErasure, lastDot + 1, returnErasure.length);
1407                        returnQualification = CharOperation.subarray(returnErasure, 0, lastDot);
1408                        if (!method.isBinary()) {
1409                            // prefix with a '*' as the full qualification could be bigger (because of an import)
1410
CharOperation.concat(IIndexConstants.ONE_STAR, returnQualification);
1411                        }
1412                    }
1413                } catch (JavaModelException e) {
1414                    return null;
1415                }
1416            }
1417            String JavaDoc[] parameterTypes = method.getParameterTypes();
1418            int paramCount = parameterTypes.length;
1419            char[][] parameterSimpleNames = new char[paramCount][];
1420            char[][] parameterQualifications = new char[paramCount][];
1421            String JavaDoc[] parameterSignatures = new String JavaDoc[paramCount];
1422            for (int i = 0; i < paramCount; i++) {
1423                parameterSignatures[i] = parameterTypes[i];
1424                char[] signature = parameterSignatures[i].toCharArray();
1425                char[] paramErasure = Signature.toCharArray(Signature.getTypeErasure(signature));
1426                CharOperation.replace(paramErasure, '$', '.');
1427                if ((lastDot = CharOperation.lastIndexOf('.', paramErasure)) == -1) {
1428                    parameterSimpleNames[i] = paramErasure;
1429                    parameterQualifications[i] = null;
1430                } else {
1431                    parameterSimpleNames[i] = CharOperation.subarray(paramErasure, lastDot + 1, paramErasure.length);
1432                    parameterQualifications[i] = CharOperation.subarray(paramErasure, 0, lastDot);
1433                    if (!method.isBinary()) {
1434                        // prefix with a '*' as the full qualification could be bigger (because of an import)
1435
CharOperation.concat(IIndexConstants.ONE_STAR, parameterQualifications[i]);
1436                    }
1437                }
1438            }
1439
1440            // Create method/constructor pattern
1441
boolean findMethodDeclarations = true;
1442            boolean findMethodReferences = true;
1443            switch (maskedLimitTo) {
1444                case IJavaSearchConstants.DECLARATIONS :
1445                    findMethodReferences = false;
1446                    break;
1447                case IJavaSearchConstants.REFERENCES :
1448                    findMethodDeclarations = false;
1449                    break;
1450                case IJavaSearchConstants.ALL_OCCURRENCES :
1451                    break;
1452            }
1453            if (isConstructor) {
1454                searchPattern =
1455                    new ConstructorPattern(
1456                        findMethodDeclarations,
1457                        findMethodReferences,
1458                        declaringSimpleName,
1459                        declaringQualification,
1460                        parameterQualifications,
1461                        parameterSimpleNames,
1462                        parameterSignatures,
1463                        method,
1464                        matchRule);
1465            } else {
1466                searchPattern =
1467                    new MethodPattern(
1468                        findMethodDeclarations,
1469                        findMethodReferences,
1470                        selector,
1471                        declaringQualification,
1472                        declaringSimpleName,
1473                        returnQualification,
1474                        returnSimpleName,
1475                        returnSignature,
1476                        parameterQualifications,
1477                        parameterSimpleNames,
1478                        parameterSignatures,
1479                        method,
1480                        matchRule);
1481            }
1482            break;
1483        case IJavaElement.TYPE :
1484            IType type = (IType)element;
1485            searchPattern = createTypePattern(
1486                        type.getElementName().toCharArray(),
1487                        type.getPackageFragment().getElementName().toCharArray(),
1488                        ignoreDeclaringType ? null : enclosingTypeNames(type),
1489                        null,
1490                        type,
1491                        maskedLimitTo,
1492                        matchRule);
1493            break;
1494        case IJavaElement.PACKAGE_DECLARATION :
1495        case IJavaElement.PACKAGE_FRAGMENT :
1496            searchPattern = createPackagePattern(element.getElementName(), maskedLimitTo, matchRule);
1497            break;
1498    }
1499    if (searchPattern != null)
1500        MatchLocator.setFocus(searchPattern, element);
1501    return searchPattern;
1502}
1503
1504private static SearchPattern createTypePattern(char[] simpleName, char[] packageName, char[][] enclosingTypeNames, String JavaDoc typeSignature, IType type, int limitTo, int matchRule) {
1505    switch (limitTo) {
1506        case IJavaSearchConstants.DECLARATIONS :
1507            return new TypeDeclarationPattern(
1508                packageName,
1509                enclosingTypeNames,
1510                simpleName,
1511                IIndexConstants.TYPE_SUFFIX,
1512                matchRule);
1513        case IJavaSearchConstants.REFERENCES :
1514            if (type != null) {
1515                return new TypeReferencePattern(
1516                    CharOperation.concatWith(packageName, enclosingTypeNames, '.'),
1517                    simpleName,
1518                    type,
1519                    matchRule);
1520            }
1521            return new TypeReferencePattern(
1522                CharOperation.concatWith(packageName, enclosingTypeNames, '.'),
1523                simpleName,
1524                typeSignature,
1525                matchRule);
1526        case IJavaSearchConstants.IMPLEMENTORS :
1527            return new SuperTypeReferencePattern(
1528                CharOperation.concatWith(packageName, enclosingTypeNames, '.'),
1529                simpleName,
1530                SuperTypeReferencePattern.ONLY_SUPER_INTERFACES,
1531                matchRule);
1532        case IJavaSearchConstants.ALL_OCCURRENCES :
1533            return new OrPattern(
1534                new TypeDeclarationPattern(
1535                    packageName,
1536                    enclosingTypeNames,
1537                    simpleName,
1538                    IIndexConstants.TYPE_SUFFIX,
1539                    matchRule),
1540                (type != null)
1541                    ? new TypeReferencePattern(
1542                        CharOperation.concatWith(packageName, enclosingTypeNames, '.'),
1543                        simpleName,
1544                        type,
1545                        matchRule)
1546                    : new TypeReferencePattern(
1547                        CharOperation.concatWith(packageName, enclosingTypeNames, '.'),
1548                        simpleName,
1549                        typeSignature,
1550                        matchRule)
1551            );
1552    }
1553    return null;
1554}
1555/**
1556 * Type pattern are formed by [qualification '.']type [typeArguments].
1557 * e.g. java.lang.Object
1558 * Runnable
1559 * List&lt;String&gt;
1560 *
1561 * @since 3.1
1562 * Type arguments can be specified to search references to parameterized types.
1563 * and look as follow: '&lt;' { [ '?' {'extends'|'super'} ] type ( ',' [ '?' {'extends'|'super'} ] type )* | '?' } '&gt;'
1564 * Please note that:
1565 * - '*' is not valid inside type arguments definition &lt;&gt;
1566 * - '?' is treated as a wildcard when it is inside &lt;&gt; (ie. it must be put on first position of the type argument)
1567 */

1568private static SearchPattern createTypePattern(String JavaDoc patternString, int limitTo, int matchRule, char indexSuffix) {
1569    
1570    Scanner scanner = new Scanner(false /*comment*/, true /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3/*sourceLevel*/, null /*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/);
1571    scanner.setSource(patternString.toCharArray());
1572    String JavaDoc type = null;
1573    int token;
1574    try {
1575        token = scanner.getNextToken();
1576    } catch (InvalidInputException e) {
1577        return null;
1578    }
1579    int argCount = 0;
1580    while (token != TerminalTokens.TokenNameEOF) {
1581        if (argCount == 0) {
1582            switch (token) {
1583                case TerminalTokens.TokenNameWHITESPACE:
1584                    break;
1585                case TerminalTokens.TokenNameLESS:
1586                    argCount++;
1587                    // fall through default case to add token to type
1588
default: // all other tokens are considered identifiers (see bug 21763 Problem in Java search [search])
1589
if (type == null)
1590                        type = scanner.getCurrentTokenString();
1591                    else
1592                        type += scanner.getCurrentTokenString();
1593            }
1594        } else {
1595            switch (token) {
1596                case TerminalTokens.TokenNameGREATER:
1597                case TerminalTokens.TokenNameRIGHT_SHIFT:
1598                case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT:
1599                    argCount--;
1600                    break;
1601                case TerminalTokens.TokenNameLESS:
1602                    argCount++;
1603                    break;
1604            }
1605            if (type == null) return null; // invalid syntax
1606
type += scanner.getCurrentTokenString();
1607        }
1608        try {
1609            token = scanner.getNextToken();
1610        } catch (InvalidInputException e) {
1611            return null;
1612        }
1613    }
1614    if (type == null) return null;
1615    String JavaDoc typeSignature = null;
1616    char[] qualificationChars = null, typeChars = null;
1617
1618    // get type part and signature
1619
char[] typePart = null;
1620    try {
1621        typeSignature = Signature.createTypeSignature(type, false);
1622        if (typeSignature.indexOf(Signature.C_GENERIC_START) < 0) {
1623            typePart = type.toCharArray();
1624        } else {
1625            typePart = Signature.toCharArray(Signature.getTypeErasure(typeSignature.toCharArray()));
1626        }
1627    }
1628    catch (IllegalArgumentException JavaDoc iae) {
1629        // string is not a valid type syntax
1630
return null;
1631    }
1632
1633    // get qualification name
1634
int lastDotPosition = CharOperation.lastIndexOf('.', typePart);
1635    if (lastDotPosition >= 0) {
1636        qualificationChars = CharOperation.subarray(typePart, 0, lastDotPosition);
1637        if (qualificationChars.length == 1 && qualificationChars[0] == '*')
1638            qualificationChars = null;
1639        typeChars = CharOperation.subarray(typePart, lastDotPosition+1, typePart.length);
1640    } else {
1641        typeChars = typePart;
1642    }
1643    if (typeChars.length == 1 && typeChars[0] == '*') {
1644        typeChars = null;
1645    }
1646    switch (limitTo) {
1647        case IJavaSearchConstants.DECLARATIONS : // cannot search for explicit member types
1648
return new QualifiedTypeDeclarationPattern(qualificationChars, typeChars, indexSuffix, matchRule);
1649        case IJavaSearchConstants.REFERENCES :
1650            return new TypeReferencePattern(qualificationChars, typeChars, typeSignature, matchRule);
1651        case IJavaSearchConstants.IMPLEMENTORS :
1652            return new SuperTypeReferencePattern(qualificationChars, typeChars, SuperTypeReferencePattern.ONLY_SUPER_INTERFACES, indexSuffix, matchRule);
1653        case IJavaSearchConstants.ALL_OCCURRENCES :
1654            return new OrPattern(
1655                new QualifiedTypeDeclarationPattern(qualificationChars, typeChars, indexSuffix, matchRule),// cannot search for explicit member types
1656
new TypeReferencePattern(qualificationChars, typeChars, matchRule));
1657    }
1658    return null;
1659}
1660/**
1661 * Returns the enclosing type names of the given type.
1662 */

1663private static char[][] enclosingTypeNames(IType type) {
1664    IJavaElement parent = type.getParent();
1665    switch (parent.getElementType()) {
1666        case IJavaElement.CLASS_FILE:
1667            // For a binary type, the parent is not the enclosing type, but the declaring type is.
1668
// (see bug 20532 Declaration of member binary type not found)
1669
IType declaringType = type.getDeclaringType();
1670            if (declaringType == null) return CharOperation.NO_CHAR_CHAR;
1671            return CharOperation.arrayConcat(
1672                enclosingTypeNames(declaringType),
1673                declaringType.getElementName().toCharArray());
1674        case IJavaElement.COMPILATION_UNIT:
1675            return CharOperation.NO_CHAR_CHAR;
1676        case IJavaElement.FIELD:
1677        case IJavaElement.INITIALIZER:
1678        case IJavaElement.METHOD:
1679            IType declaringClass = ((IMember) parent).getDeclaringType();
1680            return CharOperation.arrayConcat(
1681                enclosingTypeNames(declaringClass),
1682                new char[][] {declaringClass.getElementName().toCharArray(), IIndexConstants.ONE_STAR});
1683        case IJavaElement.TYPE:
1684            return CharOperation.arrayConcat(
1685                enclosingTypeNames((IType)parent),
1686                parent.getElementName().toCharArray());
1687        default:
1688            return null;
1689    }
1690}
1691
1692/**
1693 * Decode the given index key in this pattern. The decoded index key is used by
1694 * {@link #matchesDecodedKey(SearchPattern)} to find out if the corresponding index entry
1695 * should be considered.
1696 * <p>
1697 * This method should be re-implemented in subclasses that need to decode an index key.
1698 * </p>
1699 *
1700 * @param key the given index key
1701 */

1702public void decodeIndexKey(char[] key) {
1703    // called from findIndexMatches(), override as necessary
1704
}
1705/**
1706 * Returns a blank pattern that can be used as a record to decode an index key.
1707 * <p>
1708 * Implementors of this method should return a new search pattern that is going to be used
1709 * to decode index keys.
1710 * </p>
1711 *
1712 * @return a new blank pattern
1713 * @see #decodeIndexKey(char[])
1714 */

1715public abstract SearchPattern getBlankPattern();
1716/**
1717 * Returns a key to find in relevant index categories, if null then all index entries are matched.
1718 * The key will be matched according to some match rule. These potential matches
1719 * will be further narrowed by the match locator, but precise match locating can be expensive,
1720 * and index query should be as accurate as possible so as to eliminate obvious false hits.
1721 * <p>
1722 * This method should be re-implemented in subclasses that need to narrow down the
1723 * index query.
1724 * </p>
1725 *
1726 * @return an index key from this pattern, or <code>null</code> if all index entries are matched.
1727 */

1728public char[] getIndexKey() {
1729    return null; // called from queryIn(), override as necessary
1730
}
1731/**
1732 * Returns an array of index categories to consider for this index query.
1733 * These potential matches will be further narrowed by the match locator, but precise
1734 * match locating can be expensive, and index query should be as accurate as possible
1735 * so as to eliminate obvious false hits.
1736 * <p>
1737 * This method should be re-implemented in subclasses that need to narrow down the
1738 * index query.
1739 * </p>
1740 *
1741 * @return an array of index categories
1742 */

1743public char[][] getIndexCategories() {
1744    return CharOperation.NO_CHAR_CHAR; // called from queryIn(), override as necessary
1745
}
1746/**
1747 * Returns the rule to apply for matching index keys. Can be exact match, prefix match, pattern match or regexp match.
1748 * Rule can also be combined with a case sensitivity flag.
1749 *
1750 * @return one of R_EXACT_MATCH, R_PREFIX_MATCH, R_PATTERN_MATCH, R_REGEXP_MATCH combined with R_CASE_SENSITIVE,
1751 * e.g. R_EXACT_MATCH | R_CASE_SENSITIVE if an exact and case sensitive match is requested,
1752 * or R_PREFIX_MATCH if a prefix non case sensitive match is requested.
1753 * [TODO (frederic) I hope R_ERASURE_MATCH doesn't need to be on this list. Because it would be a breaking API change.]
1754 */

1755public final int getMatchRule() {
1756    return this.matchRule;
1757}
1758/**
1759 * Returns whether this pattern matches the given pattern (representing a decoded index key).
1760 * <p>
1761 * This method should be re-implemented in subclasses that need to narrow down the
1762 * index query.
1763 * </p>
1764 *
1765 * @param decodedPattern a pattern representing a decoded index key
1766 * @return whether this pattern matches the given pattern
1767 */

1768public boolean matchesDecodedKey(SearchPattern decodedPattern) {
1769    return true; // called from findIndexMatches(), override as necessary if index key is encoded
1770
}
1771
1772/**
1773 * Returns whether the given name matches the given pattern.
1774 * <p>
1775 * This method should be re-implemented in subclasses that need to define how
1776 * a name matches a pattern.
1777 * </p>
1778 *
1779 * @param pattern the given pattern, or <code>null</code> to represent "*"
1780 * @param name the given name
1781 * @return whether the given name matches the given pattern
1782 */

1783public boolean matchesName(char[] pattern, char[] name) {
1784    if (pattern == null) return true; // null is as if it was "*"
1785
if (name != null) {
1786        boolean isCaseSensitive = (this.matchRule & R_CASE_SENSITIVE) != 0;
1787        boolean isCamelCase = (this.matchRule & R_CAMELCASE_MATCH) != 0;
1788        int matchMode = this.matchRule & MODE_MASK;
1789        boolean emptyPattern = pattern.length == 0;
1790        if (matchMode == R_PREFIX_MATCH && emptyPattern) return true;
1791        boolean sameLength = pattern.length == name.length;
1792        boolean canBePrefix = name.length >= pattern.length;
1793        boolean matchFirstChar = !isCaseSensitive || emptyPattern || (name.length > 0 && pattern[0] == name[0]);
1794        if (isCamelCase && matchFirstChar && CharOperation.camelCaseMatch(pattern, name)) {
1795            return true;
1796        }
1797        switch (matchMode) {
1798            case R_EXACT_MATCH :
1799            case R_FULL_MATCH :
1800                if (!isCamelCase) {
1801                    if (sameLength && matchFirstChar) {
1802                        return CharOperation.equals(pattern, name, isCaseSensitive);
1803                    }
1804                    break;
1805                }
1806                // fall through next case to match as prefix if camel case failed
1807
case R_PREFIX_MATCH :
1808                if (canBePrefix && matchFirstChar) {
1809                    return CharOperation.prefixEquals(pattern, name, isCaseSensitive);
1810                }
1811                break;
1812
1813            case R_PATTERN_MATCH :
1814                if (!isCaseSensitive)
1815                    pattern = CharOperation.toLowerCase(pattern);
1816                return CharOperation.match(pattern, name, isCaseSensitive);
1817
1818            case R_REGEXP_MATCH :
1819                // TODO (frederic) implement regular expression match
1820
return true;
1821        }
1822    }
1823    return false;
1824}
1825
1826/**
1827 * Validate compatibility between given string pattern and match rule.
1828 *<br>
1829 * Optimized (ie. returned match rule is modified) combinations are:
1830 * <ul>
1831 * <li>{@link #R_PATTERN_MATCH} without any '*' or '?' in string pattern:
1832 * pattern match bit is unset,
1833 * </li>
1834 * <li>{@link #R_PATTERN_MATCH} and {@link #R_PREFIX_MATCH} bits simultaneously set:
1835 * prefix match bit is unset,
1836 * </li>
1837 * <li>{@link #R_PATTERN_MATCH} and {@link #R_CAMELCASE_MATCH} bits simultaneously set:
1838 * camel case match bit is unset,
1839 * </li>
1840 * <li>{@link #R_CAMELCASE_MATCH} with invalid combination of uppercase and lowercase characters:
1841 * camel case match bit is unset and replaced with prefix match pattern,
1842 * </li>
1843 * <li>{@link #R_CAMELCASE_MATCH} combined with {@link #R_PREFIX_MATCH} and {@link #R_CASE_SENSITIVE}
1844 * bits is reduced to only {@link #R_CAMELCASE_MATCH} as Camel Case search is already prefix and case sensitive,
1845 * </li>
1846 * </ul>
1847 *<br>
1848 * Rejected (ie. returned match rule -1) combinations are:
1849 * <ul>
1850 * <li>{@link #R_REGEXP_MATCH} with any other match mode bit set,
1851 * </li>
1852 * </ul>
1853 *
1854 * @param stringPattern The string pattern
1855 * @param matchRule The match rule
1856 * @return Optimized valid match rule or -1 if an incompatibility was detected.
1857 * @since 3.2
1858 */

1859public static int validateMatchRule(String JavaDoc stringPattern, int matchRule) {
1860
1861    // Verify Regexp match rule
1862
if ((matchRule & R_REGEXP_MATCH) != 0) {
1863        if ((matchRule & R_PATTERN_MATCH) != 0 || (matchRule & R_PREFIX_MATCH) != 0 || (matchRule & R_CAMELCASE_MATCH) != 0) {
1864            return -1;
1865        }
1866    }
1867
1868    // Verify Pattern match rule
1869
int starIndex = stringPattern.indexOf('*');
1870    int questionIndex = stringPattern.indexOf('?');
1871    if (starIndex < 0 && questionIndex < 0) {
1872        // reset pattern match bit if any
1873
matchRule &= ~R_PATTERN_MATCH;
1874    } else {
1875        // force Pattern rule
1876
matchRule |= R_PATTERN_MATCH;
1877    }
1878    if ((matchRule & R_PATTERN_MATCH) != 0) {
1879        // remove Camel Case and Prefix match bits if any
1880
matchRule &= ~R_CAMELCASE_MATCH;
1881        matchRule &= ~R_PREFIX_MATCH;
1882    }
1883
1884    // Verify Camel Case match rule
1885
if ((matchRule & R_CAMELCASE_MATCH) != 0) {
1886        // Verify sting pattern validity
1887
int length = stringPattern.length();
1888        boolean validCamelCase = true;
1889        boolean uppercase = false;
1890        for (int i=0; i<length && validCamelCase; i++) {
1891            char ch = stringPattern.charAt(i);
1892            validCamelCase = ScannerHelper.isJavaIdentifierStart(ch);
1893            // at least one uppercase character is need in CamelCase pattern
1894
// (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=136313)
1895
if (!uppercase) uppercase = ScannerHelper.isUpperCase(ch);
1896        }
1897        validCamelCase = validCamelCase && uppercase;
1898        // Verify bits compatibility
1899
if (validCamelCase) {
1900            if ((matchRule & R_PREFIX_MATCH) != 0) {
1901                if ((matchRule & R_CASE_SENSITIVE) != 0) {
1902                    // This is equivalent to Camel Case match rule
1903
matchRule &= ~R_PREFIX_MATCH;
1904                    matchRule &= ~R_CASE_SENSITIVE;
1905                }
1906            }
1907        } else {
1908            matchRule &= ~R_CAMELCASE_MATCH;
1909            if ((matchRule & R_PREFIX_MATCH) == 0) {
1910                matchRule |= R_PREFIX_MATCH;
1911                matchRule |= R_CASE_SENSITIVE;
1912            }
1913        }
1914    }
1915    return matchRule;
1916}
1917
1918/**
1919 * @see java.lang.Object#toString()
1920 */

1921public String JavaDoc toString() {
1922    return "SearchPattern"; //$NON-NLS-1$
1923
}
1924}
1925
Popular Tags