001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.core.search.matching;
011:
012: import org.eclipse.core.runtime.CoreException;
013: import org.eclipse.jdt.core.*;
014: import org.eclipse.jdt.core.compiler.CharOperation;
015: import org.eclipse.jdt.core.search.*;
016: import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
017: import org.eclipse.jdt.internal.compiler.env.*;
018: import org.eclipse.jdt.internal.compiler.lookup.*;
019: import org.eclipse.jdt.internal.core.*;
020: import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants;
021:
022: public class ClassFileMatchLocator implements IIndexConstants {
023:
024: public static char[] convertClassFileFormat(char[] name) {
025: return CharOperation.replaceOnCopy(name, '/', '.');
026: }
027:
028: private boolean checkDeclaringType(IBinaryType enclosingBinaryType,
029: char[] simpleName, char[] qualification,
030: boolean isCaseSensitive, boolean isCamelCase) {
031: if (simpleName == null && qualification == null)
032: return true;
033: if (enclosingBinaryType == null)
034: return true;
035:
036: char[] declaringTypeName = convertClassFileFormat(enclosingBinaryType
037: .getName());
038: return checkTypeName(simpleName, qualification,
039: declaringTypeName, isCaseSensitive, isCamelCase);
040: }
041:
042: private boolean checkParameters(char[] methodDescriptor,
043: char[][] parameterSimpleNames,
044: char[][] parameterQualifications, boolean isCaseSensitive,
045: boolean isCamelCase) {
046: char[][] arguments = Signature
047: .getParameterTypes(methodDescriptor);
048: int parameterCount = parameterSimpleNames.length;
049: if (parameterCount != arguments.length)
050: return false;
051: for (int i = 0; i < parameterCount; i++)
052: if (!checkTypeName(parameterSimpleNames[i],
053: parameterQualifications[i], Signature
054: .toCharArray(arguments[i]),
055: isCaseSensitive, isCamelCase))
056: return false;
057: return true;
058: }
059:
060: private boolean checkTypeName(char[] simpleName,
061: char[] qualification, char[] fullyQualifiedTypeName,
062: boolean isCaseSensitive, boolean isCamelCase) {
063: // NOTE: if case insensitive then simpleName & qualification are assumed to be lowercase
064: char[] wildcardPattern = PatternLocator.qualifiedPattern(
065: simpleName, qualification);
066: if (wildcardPattern == null)
067: return true;
068: return CharOperation.match(wildcardPattern,
069: fullyQualifiedTypeName, isCaseSensitive);
070: }
071:
072: /**
073: * Locate declaration in the current class file. This class file is always in a jar.
074: */
075: public void locateMatches(MatchLocator locator,
076: ClassFile classFile, IBinaryType info) throws CoreException {
077: // check class definition
078: SearchPattern pattern = locator.pattern;
079: BinaryType binaryType = (BinaryType) classFile.getType();
080: if (matchBinary(pattern, info, null)) {
081: binaryType = new ResolvedBinaryType(
082: (JavaElement) binaryType.getParent(), binaryType
083: .getElementName(), binaryType.getKey());
084: locator.reportBinaryMemberDeclaration(null, binaryType,
085: null, info, SearchMatch.A_ACCURATE);
086: return;
087: }
088:
089: // Define arrays to store methods/fields from binary type if necessary
090: IBinaryMethod[] binaryMethods = info.getMethods();
091: int bMethodsLength = binaryMethods == null ? 0
092: : binaryMethods.length;
093: IBinaryMethod[] unresolvedMethods = null;
094: char[][] binaryMethodSignatures = null;
095: boolean hasUnresolvedMethods = false;
096:
097: // Get fields from binary type info
098: IBinaryField[] binaryFields = info.getFields();
099: int bFieldsLength = binaryFields == null ? 0
100: : binaryFields.length;
101: IBinaryField[] unresolvedFields = null;
102: boolean hasUnresolvedFields = false;
103:
104: // Report as many accurate matches as possible
105: int accuracy = SearchMatch.A_ACCURATE;
106: boolean mustResolve = ((InternalSearchPattern) pattern).mustResolve;
107: if (mustResolve) {
108: BinaryTypeBinding binding = locator.cacheBinaryType(
109: binaryType, info);
110: if (binding != null) {
111: // filter out element not in hierarchy scope
112: if (!locator.typeInHierarchy(binding))
113: return;
114:
115: // Search matches on resolved methods
116: MethodBinding[] availableMethods = binding
117: .availableMethods();
118: int aMethodsLength = availableMethods == null ? 0
119: : availableMethods.length;
120: hasUnresolvedMethods = bMethodsLength != aMethodsLength;
121: for (int i = 0; i < aMethodsLength; i++) {
122: MethodBinding method = availableMethods[i];
123: char[] methodSignature = method.genericSignature();
124: if (methodSignature == null)
125: methodSignature = method.signature();
126:
127: // Report the match if possible
128: int level = locator.patternLocator
129: .resolveLevel(method);
130: if (level != PatternLocator.IMPOSSIBLE_MATCH) {
131: IMethod methodHandle = binaryType
132: .getMethod(
133: new String(
134: method.isConstructor() ? binding.compoundName[binding.compoundName.length - 1]
135: : method.selector),
136: CharOperation
137: .toStrings(Signature
138: .getParameterTypes(convertClassFileFormat(methodSignature))));
139: accuracy = level == PatternLocator.ACCURATE_MATCH ? SearchMatch.A_ACCURATE
140: : SearchMatch.A_INACCURATE;
141: locator.reportBinaryMemberDeclaration(null,
142: methodHandle, method, info, accuracy);
143: }
144:
145: // Remove method from unresolved list
146: if (hasUnresolvedMethods) {
147: if (binaryMethodSignatures == null) { // Store binary method signatures to avoid multiple computation
148: binaryMethodSignatures = new char[bMethodsLength][];
149: for (int j = 0; j < bMethodsLength; j++) {
150: IBinaryMethod binaryMethod = binaryMethods[j];
151: char[] signature = binaryMethod
152: .getGenericSignature();
153: if (signature == null)
154: signature = binaryMethod
155: .getMethodDescriptor();
156: binaryMethodSignatures[j] = signature;
157: }
158: }
159: for (int j = 0; j < bMethodsLength; j++) {
160: if (CharOperation.equals(binaryMethods[j]
161: .getSelector(), method.selector)
162: && CharOperation.equals(
163: binaryMethodSignatures[j],
164: methodSignature)) {
165: if (unresolvedMethods == null) {
166: System
167: .arraycopy(
168: binaryMethods,
169: 0,
170: unresolvedMethods = new IBinaryMethod[bMethodsLength],
171: 0, bMethodsLength);
172: }
173: unresolvedMethods[j] = null;
174: break;
175: }
176: }
177: }
178: }
179:
180: // Search matches on resolved fields
181: FieldBinding[] availableFields = binding
182: .availableFields();
183: int aFieldsLength = availableFields == null ? 0
184: : availableFields.length;
185: hasUnresolvedFields = bFieldsLength != aFieldsLength;
186: for (int i = 0; i < aFieldsLength; i++) {
187: FieldBinding field = availableFields[i];
188:
189: // Report the match if possible
190: int level = locator.patternLocator
191: .resolveLevel(field);
192: if (level != PatternLocator.IMPOSSIBLE_MATCH) {
193: IField fieldHandle = binaryType
194: .getField(new String(field.name));
195: accuracy = level == PatternLocator.ACCURATE_MATCH ? SearchMatch.A_ACCURATE
196: : SearchMatch.A_INACCURATE;
197: locator.reportBinaryMemberDeclaration(null,
198: fieldHandle, field, info, accuracy);
199: }
200:
201: // Remove the field from unresolved list
202: if (hasUnresolvedFields) {
203: for (int j = 0; j < bFieldsLength; j++) {
204: if (CharOperation.equals(binaryFields[j]
205: .getName(), field.name)) {
206: if (unresolvedFields == null) {
207: System
208: .arraycopy(
209: binaryFields,
210: 0,
211: unresolvedFields = new IBinaryField[bFieldsLength],
212: 0, bFieldsLength);
213: }
214: unresolvedFields[j] = null;
215: break;
216: }
217: }
218: }
219: }
220:
221: // If all methods/fields were accurate then returns now
222: if (!hasUnresolvedMethods && !hasUnresolvedFields) {
223: return;
224: }
225: }
226: accuracy = SearchMatch.A_INACCURATE;
227: }
228:
229: // Report inaccurate methods
230: if (mustResolve)
231: binaryMethods = unresolvedMethods;
232: bMethodsLength = binaryMethods == null ? 0
233: : binaryMethods.length;
234: for (int i = 0; i < bMethodsLength; i++) {
235: IBinaryMethod method = binaryMethods[i];
236: if (method == null)
237: continue; // impossible match or already reported as accurate
238: if (matchBinary(pattern, method, info)) {
239: char[] name;
240: if (method.isConstructor()) {
241: name = info.getName();
242: int lastSlash = CharOperation
243: .lastIndexOf('/', name);
244: if (lastSlash != -1) {
245: name = CharOperation.subarray(name,
246: lastSlash + 1, name.length);
247: }
248: } else {
249: name = method.getSelector();
250: }
251: String selector = new String(name);
252: char[] methodSignature = binaryMethodSignatures == null ? null
253: : binaryMethodSignatures[i];
254: if (methodSignature == null) {
255: methodSignature = method.getGenericSignature();
256: if (methodSignature == null)
257: methodSignature = method.getMethodDescriptor();
258: }
259: String[] parameterTypes = CharOperation
260: .toStrings(Signature
261: .getParameterTypes(convertClassFileFormat(methodSignature)));
262: IMethod methodHandle = binaryType.getMethod(selector,
263: parameterTypes);
264: methodHandle = new ResolvedBinaryMethod(binaryType,
265: selector, parameterTypes, methodHandle.getKey());
266: locator.reportBinaryMemberDeclaration(null,
267: methodHandle, null, info, accuracy);
268: }
269: }
270:
271: // Report inaccurate fields
272: if (mustResolve)
273: binaryFields = unresolvedFields;
274: bFieldsLength = binaryFields == null ? 0 : binaryFields.length;
275: for (int i = 0; i < bFieldsLength; i++) {
276: IBinaryField field = binaryFields[i];
277: if (field == null)
278: continue; // impossible match or already reported as accurate
279: if (matchBinary(pattern, field, info)) {
280: String fieldName = new String(field.getName());
281: IField fieldHandle = binaryType.getField(fieldName);
282: fieldHandle = new ResolvedBinaryField(binaryType,
283: fieldName, fieldHandle.getKey());
284: locator.reportBinaryMemberDeclaration(null,
285: fieldHandle, null, info, accuracy);
286: }
287: }
288: }
289:
290: /**
291: * Finds out whether the given binary info matches the search pattern.
292: * Default is to return false.
293: */
294: boolean matchBinary(SearchPattern pattern, Object binaryInfo,
295: IBinaryType enclosingBinaryType) {
296: switch (((InternalSearchPattern) pattern).kind) {
297: case CONSTRUCTOR_PATTERN:
298: return matchConstructor((ConstructorPattern) pattern,
299: binaryInfo, enclosingBinaryType);
300: case FIELD_PATTERN:
301: return matchField((FieldPattern) pattern, binaryInfo,
302: enclosingBinaryType);
303: case METHOD_PATTERN:
304: return matchMethod((MethodPattern) pattern, binaryInfo,
305: enclosingBinaryType);
306: case SUPER_REF_PATTERN:
307: return matchSuperTypeReference(
308: (SuperTypeReferencePattern) pattern, binaryInfo,
309: enclosingBinaryType);
310: case TYPE_DECL_PATTERN:
311: return matchTypeDeclaration(
312: (TypeDeclarationPattern) pattern, binaryInfo,
313: enclosingBinaryType);
314: case OR_PATTERN:
315: SearchPattern[] patterns = ((OrPattern) pattern).patterns;
316: for (int i = 0, length = patterns.length; i < length; i++)
317: if (matchBinary(patterns[i], binaryInfo,
318: enclosingBinaryType))
319: return true;
320: }
321: return false;
322: }
323:
324: boolean matchConstructor(ConstructorPattern pattern,
325: Object binaryInfo, IBinaryType enclosingBinaryType) {
326: if (!pattern.findDeclarations)
327: return false; // only relevant when finding declarations
328: if (!(binaryInfo instanceof IBinaryMethod))
329: return false;
330:
331: IBinaryMethod method = (IBinaryMethod) binaryInfo;
332: if (!method.isConstructor())
333: return false;
334: if (!checkDeclaringType(enclosingBinaryType,
335: pattern.declaringSimpleName,
336: pattern.declaringQualification, pattern
337: .isCaseSensitive(), pattern.isCamelCase()))
338: return false;
339: if (pattern.parameterSimpleNames != null) {
340: char[] methodDescriptor = convertClassFileFormat(method
341: .getMethodDescriptor());
342: if (!checkParameters(methodDescriptor,
343: pattern.parameterSimpleNames,
344: pattern.parameterQualifications, pattern
345: .isCaseSensitive(), pattern.isCamelCase()))
346: return false;
347: }
348: return true;
349: }
350:
351: boolean matchField(FieldPattern pattern, Object binaryInfo,
352: IBinaryType enclosingBinaryType) {
353: if (!pattern.findDeclarations)
354: return false; // only relevant when finding declarations
355: if (!(binaryInfo instanceof IBinaryField))
356: return false;
357:
358: IBinaryField field = (IBinaryField) binaryInfo;
359: if (!pattern.matchesName(pattern.name, field.getName()))
360: return false;
361: if (!checkDeclaringType(enclosingBinaryType,
362: pattern.declaringSimpleName,
363: pattern.declaringQualification, pattern
364: .isCaseSensitive(), pattern.isCamelCase()))
365: return false;
366:
367: char[] fieldTypeSignature = Signature
368: .toCharArray(convertClassFileFormat(field.getTypeName()));
369: return checkTypeName(pattern.typeSimpleName,
370: pattern.typeQualification, fieldTypeSignature, pattern
371: .isCaseSensitive(), pattern.isCamelCase());
372: }
373:
374: boolean matchMethod(MethodPattern pattern, Object binaryInfo,
375: IBinaryType enclosingBinaryType) {
376: if (!pattern.findDeclarations)
377: return false; // only relevant when finding declarations
378: if (!(binaryInfo instanceof IBinaryMethod))
379: return false;
380:
381: IBinaryMethod method = (IBinaryMethod) binaryInfo;
382: if (!pattern
383: .matchesName(pattern.selector, method.getSelector()))
384: return false;
385: if (!checkDeclaringType(enclosingBinaryType,
386: pattern.declaringSimpleName,
387: pattern.declaringQualification, pattern
388: .isCaseSensitive(), pattern.isCamelCase()))
389: return false;
390:
391: // look at return type only if declaring type is not specified
392: boolean checkReturnType = pattern.declaringSimpleName == null
393: && (pattern.returnSimpleName != null || pattern.returnQualification != null);
394: boolean checkParameters = pattern.parameterSimpleNames != null;
395: if (checkReturnType || checkParameters) {
396: char[] methodDescriptor = convertClassFileFormat(method
397: .getMethodDescriptor());
398: if (checkReturnType) {
399: char[] returnTypeSignature = Signature
400: .toCharArray(Signature
401: .getReturnType(methodDescriptor));
402: if (!checkTypeName(pattern.returnSimpleName,
403: pattern.returnQualification,
404: returnTypeSignature, pattern.isCaseSensitive(),
405: pattern.isCamelCase()))
406: return false;
407: }
408: if (checkParameters
409: && !checkParameters(methodDescriptor,
410: pattern.parameterSimpleNames,
411: pattern.parameterQualifications, pattern
412: .isCaseSensitive(), pattern
413: .isCamelCase()))
414: return false;
415: }
416: return true;
417: }
418:
419: boolean matchSuperTypeReference(SuperTypeReferencePattern pattern,
420: Object binaryInfo, IBinaryType enclosingBinaryType) {
421: if (!(binaryInfo instanceof IBinaryType))
422: return false;
423:
424: IBinaryType type = (IBinaryType) binaryInfo;
425: if (pattern.super RefKind != SuperTypeReferencePattern.ONLY_SUPER_INTERFACES) {
426: char[] vmName = type.getSuperclassName();
427: if (vmName != null) {
428: char[] super className = convertClassFileFormat(vmName);
429: if (checkTypeName(pattern.super SimpleName,
430: pattern.super Qualification, super className,
431: pattern.isCaseSensitive(), pattern
432: .isCamelCase()))
433: return true;
434: }
435: }
436:
437: if (pattern.super RefKind != SuperTypeReferencePattern.ONLY_SUPER_CLASSES) {
438: char[][] super Interfaces = type.getInterfaceNames();
439: if (super Interfaces != null) {
440: for (int i = 0, max = super Interfaces.length; i < max; i++) {
441: char[] super InterfaceName = convertClassFileFormat(super Interfaces[i]);
442: if (checkTypeName(pattern.super SimpleName,
443: pattern.super Qualification,
444: super InterfaceName, pattern
445: .isCaseSensitive(), pattern
446: .isCamelCase()))
447: return true;
448: }
449: }
450: }
451: return false;
452: }
453:
454: boolean matchTypeDeclaration(TypeDeclarationPattern pattern,
455: Object binaryInfo, IBinaryType enclosingBinaryType) {
456: if (!(binaryInfo instanceof IBinaryType))
457: return false;
458:
459: IBinaryType type = (IBinaryType) binaryInfo;
460: char[] fullyQualifiedTypeName = convertClassFileFormat(type
461: .getName());
462: boolean qualifiedPattern = pattern instanceof QualifiedTypeDeclarationPattern;
463: if (pattern.enclosingTypeNames == null || qualifiedPattern) {
464: char[] simpleName = (pattern.getMatchMode() == SearchPattern.R_PREFIX_MATCH) ? CharOperation
465: .concat(pattern.simpleName,
466: IIndexConstants.ONE_STAR)
467: : pattern.simpleName;
468: char[] pkg = qualifiedPattern ? ((QualifiedTypeDeclarationPattern) pattern).qualification
469: : pattern.pkg;
470: if (!checkTypeName(simpleName, pkg, fullyQualifiedTypeName,
471: pattern.isCaseSensitive(), pattern.isCamelCase()))
472: return false;
473: } else {
474: char[] enclosingTypeName = CharOperation.concatWith(
475: pattern.enclosingTypeNames, '.');
476: char[] patternString = pattern.pkg == null ? enclosingTypeName
477: : CharOperation.concat(pattern.pkg,
478: enclosingTypeName, '.');
479: if (!checkTypeName(pattern.simpleName, patternString,
480: fullyQualifiedTypeName, pattern.isCaseSensitive(),
481: pattern.isCamelCase()))
482: return false;
483: }
484:
485: int kind = TypeDeclaration.kind(type.getModifiers());
486: switch (pattern.typeSuffix) {
487: case CLASS_SUFFIX:
488: return kind == TypeDeclaration.CLASS_DECL;
489: case INTERFACE_SUFFIX:
490: return kind == TypeDeclaration.INTERFACE_DECL;
491: case ENUM_SUFFIX:
492: return kind == TypeDeclaration.ENUM_DECL;
493: case ANNOTATION_TYPE_SUFFIX:
494: return kind == TypeDeclaration.ANNOTATION_TYPE_DECL;
495: case CLASS_AND_INTERFACE_SUFFIX:
496: return kind == TypeDeclaration.CLASS_DECL
497: || kind == TypeDeclaration.INTERFACE_DECL;
498: case CLASS_AND_ENUM_SUFFIX:
499: return kind == TypeDeclaration.CLASS_DECL
500: || kind == TypeDeclaration.ENUM_DECL;
501: case INTERFACE_AND_ANNOTATION_SUFFIX:
502: return kind == TypeDeclaration.INTERFACE_DECL
503: || kind == TypeDeclaration.ANNOTATION_TYPE_DECL;
504: case TYPE_SUFFIX: // nothing
505: }
506: return true;
507: }
508: }
|