001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.core.search.matching;
011:
012: import java.io.IOException;
013:
014: import org.eclipse.jdt.core.compiler.CharOperation;
015: import org.eclipse.jdt.core.search.SearchPattern;
016: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
017: import org.eclipse.jdt.internal.core.index.*;
018:
019: public class TypeDeclarationPattern extends JavaSearchPattern {
020:
021: public char[] simpleName;
022: public char[] pkg;
023: public char[][] enclosingTypeNames;
024:
025: // set to CLASS_SUFFIX for only matching classes
026: // set to INTERFACE_SUFFIX for only matching interfaces
027: // set to ENUM_SUFFIX for only matching enums
028: // set to ANNOTATION_TYPE_SUFFIX for only matching annotation types
029: // set to TYPE_SUFFIX for matching both classes and interfaces
030: public char typeSuffix;
031: public int modifiers;
032: public boolean secondary = false;
033:
034: protected static char[][] CATEGORIES = { TYPE_DECL };
035:
036: // want to save space by interning the package names for each match
037: static PackageNameSet internedPackageNames = new PackageNameSet(
038: 1001);
039:
040: static class PackageNameSet {
041:
042: public char[][] names;
043: public int elementSize; // number of elements in the table
044: public int threshold;
045:
046: PackageNameSet(int size) {
047: this .elementSize = 0;
048: this .threshold = size; // size represents the expected number of elements
049: int extraRoom = (int) (size * 1.5f);
050: if (this .threshold == extraRoom)
051: extraRoom++;
052: this .names = new char[extraRoom][];
053: }
054:
055: char[] add(char[] name) {
056: int length = names.length;
057: int index = CharOperation.hashCode(name) % length;
058: char[] current;
059: while ((current = names[index]) != null) {
060: if (CharOperation.equals(current, name))
061: return current;
062: if (++index == length)
063: index = 0;
064: }
065: names[index] = name;
066:
067: // assumes the threshold is never equal to the size of the table
068: if (++elementSize > threshold)
069: rehash();
070: return name;
071: }
072:
073: void rehash() {
074: PackageNameSet newSet = new PackageNameSet(elementSize * 2); // double the number of expected elements
075: char[] current;
076: for (int i = names.length; --i >= 0;)
077: if ((current = names[i]) != null)
078: newSet.add(current);
079:
080: this .names = newSet.names;
081: this .elementSize = newSet.elementSize;
082: this .threshold = newSet.threshold;
083: }
084: }
085:
086: /*
087: * Create index key for type declaration pattern:
088: * key = typeName / packageName / enclosingTypeName / modifiers
089: * or for secondary types
090: * key = typeName / packageName / enclosingTypeName / modifiers / 'S'
091: */
092: public static char[] createIndexKey(int modifiers, char[] typeName,
093: char[] packageName, char[][] enclosingTypeNames,
094: boolean secondary) { //, char typeSuffix) {
095: int typeNameLength = typeName == null ? 0 : typeName.length;
096: int packageLength = packageName == null ? 0
097: : packageName.length;
098: int enclosingNamesLength = 0;
099: if (enclosingTypeNames != null) {
100: for (int i = 0, length = enclosingTypeNames.length; i < length;) {
101: enclosingNamesLength += enclosingTypeNames[i].length;
102: if (++i < length)
103: enclosingNamesLength++; // for the '.' separator
104: }
105: }
106:
107: int resultLength = typeNameLength + packageLength
108: + enclosingNamesLength + 5;
109: if (secondary)
110: resultLength += 2;
111: char[] result = new char[resultLength];
112: int pos = 0;
113: if (typeNameLength > 0) {
114: System.arraycopy(typeName, 0, result, pos, typeNameLength);
115: pos += typeNameLength;
116: }
117: result[pos++] = SEPARATOR;
118: if (packageLength > 0) {
119: System
120: .arraycopy(packageName, 0, result, pos,
121: packageLength);
122: pos += packageLength;
123: }
124: result[pos++] = SEPARATOR;
125: if (enclosingTypeNames != null && enclosingNamesLength > 0) {
126: for (int i = 0, length = enclosingTypeNames.length; i < length;) {
127: char[] enclosingName = enclosingTypeNames[i];
128: int itsLength = enclosingName.length;
129: System.arraycopy(enclosingName, 0, result, pos,
130: itsLength);
131: pos += itsLength;
132: if (++i < length)
133: result[pos++] = '.';
134: }
135: }
136: result[pos++] = SEPARATOR;
137: result[pos++] = (char) modifiers;
138: result[pos] = (char) (modifiers >> 16);
139: if (secondary) {
140: result[++pos] = SEPARATOR;
141: result[++pos] = 'S';
142: }
143: return result;
144: }
145:
146: public TypeDeclarationPattern(char[] pkg,
147: char[][] enclosingTypeNames, char[] simpleName,
148: char typeSuffix, int matchRule) {
149:
150: this (matchRule);
151:
152: this .pkg = this .isCaseSensitive ? pkg : CharOperation
153: .toLowerCase(pkg);
154: if (this .isCaseSensitive || enclosingTypeNames == null) {
155: this .enclosingTypeNames = enclosingTypeNames;
156: } else {
157: int length = enclosingTypeNames.length;
158: this .enclosingTypeNames = new char[length][];
159: for (int i = 0; i < length; i++)
160: this .enclosingTypeNames[i] = CharOperation
161: .toLowerCase(enclosingTypeNames[i]);
162: }
163: this .simpleName = (this .isCaseSensitive || this .isCamelCase) ? simpleName
164: : CharOperation.toLowerCase(simpleName);
165: this .typeSuffix = typeSuffix;
166:
167: ((InternalSearchPattern) this ).mustResolve = (this .pkg != null && this .enclosingTypeNames != null)
168: || typeSuffix != TYPE_SUFFIX;
169: }
170:
171: TypeDeclarationPattern(int matchRule) {
172: super (TYPE_DECL_PATTERN, matchRule);
173: }
174:
175: /*
176: * Type entries are encoded as:
177: * simpleTypeName / packageName / enclosingTypeName / modifiers
178: * e.g. Object/java.lang//0
179: * e.g. Cloneable/java.lang//512
180: * e.g. LazyValue/javax.swing/UIDefaults/0
181: * or for secondary types as:
182: * simpleTypeName / packageName / enclosingTypeName / modifiers / S
183: */
184: public void decodeIndexKey(char[] key) {
185: int slash = CharOperation.indexOf(SEPARATOR, key, 0);
186: this .simpleName = CharOperation.subarray(key, 0, slash);
187:
188: int start = ++slash;
189: if (key[start] == SEPARATOR) {
190: this .pkg = CharOperation.NO_CHAR;
191: } else {
192: slash = CharOperation.indexOf(SEPARATOR, key, start);
193: this .pkg = internedPackageNames.add(CharOperation.subarray(
194: key, start, slash));
195: }
196:
197: // Continue key read by the end to decode modifiers
198: int last = key.length - 1;
199: this .secondary = key[last] == 'S';
200: if (this .secondary) {
201: last -= 2;
202: }
203: this .modifiers = key[last - 1] + (key[last] << 16);
204: decodeModifiers();
205:
206: // Retrieve enclosing type names
207: start = slash + 1;
208: last -= 2; // position of ending slash
209: if (start == last) {
210: this .enclosingTypeNames = CharOperation.NO_CHAR_CHAR;
211: } else {
212: if (last == (start + 1) && key[start] == ZERO_CHAR) {
213: this .enclosingTypeNames = ONE_ZERO_CHAR;
214: } else {
215: this .enclosingTypeNames = CharOperation.splitOn('.',
216: key, start, last);
217: }
218: }
219: }
220:
221: protected void decodeModifiers() {
222:
223: // Extract suffix from modifiers instead of index key
224: switch (this .modifiers
225: & (ClassFileConstants.AccInterface
226: | ClassFileConstants.AccEnum | ClassFileConstants.AccAnnotation)) {
227: case ClassFileConstants.AccAnnotation:
228: case ClassFileConstants.AccAnnotation
229: + ClassFileConstants.AccInterface:
230: this .typeSuffix = ANNOTATION_TYPE_SUFFIX;
231: break;
232: case ClassFileConstants.AccEnum:
233: this .typeSuffix = ENUM_SUFFIX;
234: break;
235: case ClassFileConstants.AccInterface:
236: this .typeSuffix = INTERFACE_SUFFIX;
237: break;
238: default:
239: this .typeSuffix = CLASS_SUFFIX;
240: break;
241: }
242: }
243:
244: public SearchPattern getBlankPattern() {
245: return new TypeDeclarationPattern(R_EXACT_MATCH
246: | R_CASE_SENSITIVE);
247: }
248:
249: public char[][] getIndexCategories() {
250: return CATEGORIES;
251: }
252:
253: public boolean matchesDecodedKey(SearchPattern decodedPattern) {
254: TypeDeclarationPattern pattern = (TypeDeclarationPattern) decodedPattern;
255:
256: // check type suffix
257: if (this .typeSuffix != pattern.typeSuffix
258: && typeSuffix != TYPE_SUFFIX) {
259: if (!matchDifferentTypeSuffixes(this .typeSuffix,
260: pattern.typeSuffix)) {
261: return false;
262: }
263: }
264:
265: // check name
266: if (!matchesName(this .simpleName, pattern.simpleName))
267: return false;
268:
269: // check package - exact match only
270: if (this .pkg != null
271: && !CharOperation.equals(this .pkg, pattern.pkg,
272: isCaseSensitive()))
273: return false;
274:
275: // check enclosingTypeNames - exact match only
276: if (this .enclosingTypeNames != null) {
277: if (this .enclosingTypeNames.length == 0)
278: return pattern.enclosingTypeNames.length == 0;
279: if (this .enclosingTypeNames.length == 1
280: && pattern.enclosingTypeNames.length == 1)
281: return CharOperation.equals(this .enclosingTypeNames[0],
282: pattern.enclosingTypeNames[0],
283: isCaseSensitive());
284: if (pattern.enclosingTypeNames == ONE_ZERO_CHAR)
285: return true; // is a local or anonymous type
286: return CharOperation.equals(this .enclosingTypeNames,
287: pattern.enclosingTypeNames, isCaseSensitive());
288: }
289: return true;
290: }
291:
292: EntryResult[] queryIn(Index index) throws IOException {
293: char[] key = this .simpleName; // can be null
294: int matchRule = getMatchRule();
295:
296: switch (getMatchMode()) {
297: case R_PREFIX_MATCH:
298: // do a prefix query with the simpleName
299: break;
300: case R_EXACT_MATCH:
301: if (this .isCamelCase)
302: break;
303: matchRule &= ~R_EXACT_MATCH;
304: if (this .simpleName != null) {
305: matchRule |= R_PREFIX_MATCH;
306: key = this .pkg == null ? CharOperation.append(
307: this .simpleName, SEPARATOR) : CharOperation
308: .concat(this .simpleName, SEPARATOR, this .pkg,
309: SEPARATOR, CharOperation.NO_CHAR);
310: break; // do a prefix query with the simpleName and possibly the pkg
311: }
312: matchRule |= R_PATTERN_MATCH;
313: // fall thru to encode the key and do a pattern query
314: case R_PATTERN_MATCH:
315: if (this .pkg == null) {
316: if (this .simpleName == null) {
317: switch (this .typeSuffix) {
318: case CLASS_SUFFIX:
319: case INTERFACE_SUFFIX:
320: case ENUM_SUFFIX:
321: case ANNOTATION_TYPE_SUFFIX:
322: case CLASS_AND_INTERFACE_SUFFIX:
323: case CLASS_AND_ENUM_SUFFIX:
324: case INTERFACE_AND_ANNOTATION_SUFFIX:
325: // null key already returns all types
326: // key = new char[] {ONE_STAR[0], SEPARATOR, ONE_STAR[0]};
327: break;
328: }
329: } else if (this .simpleName[this .simpleName.length - 1] != '*') {
330: key = CharOperation.concat(this .simpleName,
331: ONE_STAR, SEPARATOR);
332: }
333: break; // do a pattern query with the current encoded key
334: }
335: // must decode to check enclosingTypeNames due to the encoding of local types
336: key = CharOperation.concat(
337: this .simpleName == null ? ONE_STAR
338: : this .simpleName, SEPARATOR, this .pkg,
339: SEPARATOR, ONE_STAR);
340: break;
341: case R_REGEXP_MATCH:
342: // TODO (frederic) implement regular expression match
343: break;
344: }
345:
346: return index.query(getIndexCategories(), key, matchRule); // match rule is irrelevant when the key is null
347: }
348:
349: protected StringBuffer print(StringBuffer output) {
350: switch (this .typeSuffix) {
351: case CLASS_SUFFIX:
352: output.append("ClassDeclarationPattern: pkg<"); //$NON-NLS-1$
353: break;
354: case CLASS_AND_INTERFACE_SUFFIX:
355: output.append("ClassAndInterfaceDeclarationPattern: pkg<"); //$NON-NLS-1$
356: break;
357: case CLASS_AND_ENUM_SUFFIX:
358: output.append("ClassAndEnumDeclarationPattern: pkg<"); //$NON-NLS-1$
359: break;
360: case INTERFACE_SUFFIX:
361: output.append("InterfaceDeclarationPattern: pkg<"); //$NON-NLS-1$
362: break;
363: case INTERFACE_AND_ANNOTATION_SUFFIX:
364: output
365: .append("InterfaceAndAnnotationDeclarationPattern: pkg<"); //$NON-NLS-1$
366: break;
367: case ENUM_SUFFIX:
368: output.append("EnumDeclarationPattern: pkg<"); //$NON-NLS-1$
369: break;
370: case ANNOTATION_TYPE_SUFFIX:
371: output.append("AnnotationTypeDeclarationPattern: pkg<"); //$NON-NLS-1$
372: break;
373: default:
374: output.append("TypeDeclarationPattern: pkg<"); //$NON-NLS-1$
375: break;
376: }
377: if (pkg != null)
378: output.append(pkg);
379: else
380: output.append("*"); //$NON-NLS-1$
381: output.append(">, enclosing<"); //$NON-NLS-1$
382: if (enclosingTypeNames != null) {
383: for (int i = 0; i < enclosingTypeNames.length; i++) {
384: output.append(enclosingTypeNames[i]);
385: if (i < enclosingTypeNames.length - 1)
386: output.append('.');
387: }
388: } else {
389: output.append("*"); //$NON-NLS-1$
390: }
391: output.append(">, type<"); //$NON-NLS-1$
392: if (simpleName != null)
393: output.append(simpleName);
394: else
395: output.append("*"); //$NON-NLS-1$
396: output.append(">"); //$NON-NLS-1$
397: return super.print(output);
398: }
399: }
|