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 java.util.HashMap;
013:
014: import org.eclipse.core.resources.IResource;
015: import org.eclipse.core.runtime.*;
016: import org.eclipse.jdt.core.*;
017: import org.eclipse.jdt.core.compiler.CharOperation;
018: import org.eclipse.jdt.core.search.*;
019: import org.eclipse.jdt.internal.compiler.ast.*;
020: import org.eclipse.jdt.internal.compiler.env.IBinaryType;
021: import org.eclipse.jdt.internal.compiler.lookup.*;
022: import org.eclipse.jdt.internal.compiler.util.SimpleSet;
023: import org.eclipse.jdt.internal.core.JavaElement;
024: import org.eclipse.jdt.internal.core.search.BasicSearchEngine;
025:
026: public class MethodLocator extends PatternLocator {
027:
028: protected MethodPattern pattern;
029: protected boolean isDeclarationOfReferencedMethodsPattern;
030:
031: //extra reference info
032: public char[][][] allSuperDeclaringTypeNames;
033:
034: //method declarations which parameters verification fail
035: private HashMap methodDeclarationsWithInvalidParam = new HashMap();
036:
037: public MethodLocator(MethodPattern pattern) {
038: super (pattern);
039:
040: this .pattern = pattern;
041: this .isDeclarationOfReferencedMethodsPattern = this .pattern instanceof DeclarationOfReferencedMethodsPattern;
042: }
043:
044: /*
045: * Clear caches
046: */
047: protected void clear() {
048: this .methodDeclarationsWithInvalidParam = new HashMap();
049: }
050:
051: public void initializePolymorphicSearch(MatchLocator locator) {
052: long start = 0;
053: if (BasicSearchEngine.VERBOSE) {
054: start = System.currentTimeMillis();
055: }
056: try {
057: this .allSuperDeclaringTypeNames = new SuperTypeNamesCollector(
058: this .pattern, this .pattern.declaringSimpleName,
059: this .pattern.declaringQualification, locator,
060: this .pattern.declaringType, locator.progressMonitor)
061: .collect();
062: } catch (JavaModelException e) {
063: // inaccurate matches will be found
064: }
065: if (BasicSearchEngine.VERBOSE) {
066: System.out
067: .println("Time to initialize polymorphic search: " + (System.currentTimeMillis() - start)); //$NON-NLS-1$
068: }
069: }
070:
071: /*
072: * Return whether a type name is in pattern all super declaring types names.
073: */
074: private boolean isTypeInSuperDeclaringTypeNames(char[][] typeName) {
075: if (allSuperDeclaringTypeNames == null)
076: return false;
077: int length = allSuperDeclaringTypeNames.length;
078: for (int i = 0; i < length; i++) {
079: if (CharOperation.equals(allSuperDeclaringTypeNames[i],
080: typeName)) {
081: return true;
082: }
083: }
084: return false;
085: }
086:
087: /**
088: * Returns whether the code gen will use an invoke virtual for
089: * this message send or not.
090: */
091: protected boolean isVirtualInvoke(MethodBinding method,
092: MessageSend messageSend) {
093: return !method.isStatic() && !method.isPrivate()
094: && !messageSend.isSuperAccess();
095: }
096:
097: public int match(ASTNode node, MatchingNodeSet nodeSet) {
098: int declarationsLevel = IMPOSSIBLE_MATCH;
099: if (this .pattern.findReferences) {
100: if (node instanceof ImportReference) {
101: // With static import, we can have static method reference in import reference
102: ImportReference importRef = (ImportReference) node;
103: int length = importRef.tokens.length - 1;
104: if (importRef.isStatic()
105: && ((importRef.bits & ASTNode.OnDemand) == 0)
106: && matchesName(this .pattern.selector,
107: importRef.tokens[length])) {
108: char[][] compoundName = new char[length][];
109: System.arraycopy(importRef.tokens, 0, compoundName,
110: 0, length);
111: char[] declaringType = CharOperation.concat(
112: pattern.declaringQualification,
113: pattern.declaringSimpleName, '.');
114: if (matchesName(declaringType, CharOperation
115: .concatWith(compoundName, '.'))) {
116: declarationsLevel = ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
117: : ACCURATE_MATCH;
118: }
119: }
120: }
121: }
122: return nodeSet.addMatch(node, declarationsLevel);
123: }
124:
125: //public int match(ConstructorDeclaration node, MatchingNodeSet nodeSet) - SKIP IT
126: //public int match(Expression node, MatchingNodeSet nodeSet) - SKIP IT
127: //public int match(FieldDeclaration node, MatchingNodeSet nodeSet) - SKIP IT
128: public int match(MethodDeclaration node, MatchingNodeSet nodeSet) {
129: if (!this .pattern.findDeclarations)
130: return IMPOSSIBLE_MATCH;
131:
132: // Verify method name
133: if (!matchesName(this .pattern.selector, node.selector))
134: return IMPOSSIBLE_MATCH;
135:
136: // Verify parameters types
137: boolean resolve = ((InternalSearchPattern) this .pattern).mustResolve;
138: if (this .pattern.parameterSimpleNames != null) {
139: int length = this .pattern.parameterSimpleNames.length;
140: ASTNode[] args = node.arguments;
141: int argsLength = args == null ? 0 : args.length;
142: if (length != argsLength)
143: return IMPOSSIBLE_MATCH;
144: for (int i = 0; i < argsLength; i++) {
145: if (args != null
146: && !matchesTypeReference(
147: this .pattern.parameterSimpleNames[i],
148: ((Argument) args[i]).type)) {
149: // Do not return as impossible when source level is at least 1.5
150: if (this .mayBeGeneric) {
151: if (!((InternalSearchPattern) this .pattern).mustResolve) {
152: // Set resolution flag on node set in case of types was inferred in parameterized types from generic ones...
153: // (see bugs https://bugs.eclipse.org/bugs/show_bug.cgi?id=79990, 96761, 96763)
154: nodeSet.mustResolve = true;
155: resolve = true;
156: }
157: this .methodDeclarationsWithInvalidParam.put(
158: node, null);
159: } else {
160: return IMPOSSIBLE_MATCH;
161: }
162: }
163: }
164: }
165:
166: // Verify type arguments (do not reject if pattern has no argument as it can be an erasure match)
167: if (this .pattern.hasMethodArguments()) {
168: if (node.typeParameters == null
169: || node.typeParameters.length != this .pattern.methodArguments.length)
170: return IMPOSSIBLE_MATCH;
171: }
172:
173: // Method declaration may match pattern
174: return nodeSet.addMatch(node, resolve ? POSSIBLE_MATCH
175: : ACCURATE_MATCH);
176: }
177:
178: public int match(MemberValuePair node, MatchingNodeSet nodeSet) {
179: if (!this .pattern.findReferences)
180: return IMPOSSIBLE_MATCH;
181:
182: if (!matchesName(this .pattern.selector, node.name))
183: return IMPOSSIBLE_MATCH;
184:
185: return nodeSet
186: .addMatch(
187: node,
188: ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
189: : ACCURATE_MATCH);
190: }
191:
192: public int match(MessageSend node, MatchingNodeSet nodeSet) {
193: if (!this .pattern.findReferences)
194: return IMPOSSIBLE_MATCH;
195:
196: if (!matchesName(this .pattern.selector, node.selector))
197: return IMPOSSIBLE_MATCH;
198: if (this .pattern.parameterSimpleNames != null
199: && (!this .pattern.varargs || ((node.bits & ASTNode.InsideJavadoc) != 0))) {
200: int length = this .pattern.parameterSimpleNames.length;
201: ASTNode[] args = node.arguments;
202: int argsLength = args == null ? 0 : args.length;
203: if (length != argsLength)
204: return IMPOSSIBLE_MATCH;
205: }
206:
207: return nodeSet
208: .addMatch(
209: node,
210: ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
211: : ACCURATE_MATCH);
212: }
213:
214: //public int match(Reference node, MatchingNodeSet nodeSet) - SKIP IT
215: public int match(Annotation node, MatchingNodeSet nodeSet) {
216: if (!this .pattern.findReferences)
217: return IMPOSSIBLE_MATCH;
218: MemberValuePair[] pairs = node.memberValuePairs();
219: if (pairs == null || pairs.length == 0)
220: return IMPOSSIBLE_MATCH;
221:
222: int length = pairs.length;
223: MemberValuePair pair = null;
224: for (int i = 0; i < length; i++) {
225: pair = node.memberValuePairs()[i];
226: if (matchesName(this .pattern.selector, pair.name)) {
227: ASTNode possibleNode = (node instanceof SingleMemberAnnotation) ? (ASTNode) node
228: : pair;
229: return nodeSet
230: .addMatch(
231: possibleNode,
232: ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
233: : ACCURATE_MATCH);
234: }
235: }
236: return IMPOSSIBLE_MATCH;
237: }
238:
239: //public int match(TypeDeclaration node, MatchingNodeSet nodeSet) - SKIP IT
240: //public int match(TypeReference node, MatchingNodeSet nodeSet) - SKIP IT
241:
242: protected int matchContainer() {
243: if (this .pattern.findReferences) {
244: // need to look almost everywhere to find in javadocs and static import
245: return ALL_CONTAINER;
246: }
247: return CLASS_CONTAINER;
248: }
249:
250: /* (non-Javadoc)
251: * @see org.eclipse.jdt.internal.core.search.matching.PatternLocator#matchLevelAndReportImportRef(org.eclipse.jdt.internal.compiler.ast.ImportReference, org.eclipse.jdt.internal.compiler.lookup.Binding, org.eclipse.jdt.internal.core.search.matching.MatchLocator)
252: * Accept to report match of static field on static import
253: */
254: protected void matchLevelAndReportImportRef(
255: ImportReference importRef, Binding binding,
256: MatchLocator locator) throws CoreException {
257: if (importRef.isStatic() && binding instanceof MethodBinding) {
258: super .matchLevelAndReportImportRef(importRef, binding,
259: locator);
260: }
261: }
262:
263: protected int matchMethod(MethodBinding method,
264: boolean skipImpossibleArg) {
265: if (!matchesName(this .pattern.selector, method.selector))
266: return IMPOSSIBLE_MATCH;
267:
268: int level = ACCURATE_MATCH;
269: // look at return type only if declaring type is not specified
270: if (this .pattern.declaringSimpleName == null) {
271: // TODO (frederic) use this call to refine accuracy on return type
272: // int newLevel = resolveLevelForType(this.pattern.returnSimpleName, this.pattern.returnQualification, this.pattern.returnTypeArguments, 0, method.returnType);
273: int newLevel = resolveLevelForType(
274: this .pattern.returnSimpleName,
275: this .pattern.returnQualification, method.returnType);
276: if (level > newLevel) {
277: if (newLevel == IMPOSSIBLE_MATCH)
278: return IMPOSSIBLE_MATCH;
279: level = newLevel; // can only be downgraded
280: }
281: }
282:
283: // parameter types
284: int parameterCount = this .pattern.parameterSimpleNames == null ? -1
285: : this .pattern.parameterSimpleNames.length;
286: if (parameterCount > -1) {
287: // global verification
288: if (method.parameters == null)
289: return INACCURATE_MATCH;
290: if (parameterCount != method.parameters.length)
291: return IMPOSSIBLE_MATCH;
292: if (!method.isValidBinding()
293: && ((ProblemMethodBinding) method).problemId() == ProblemReasons.Ambiguous) {
294: // return inaccurate match for ambiguous call (bug 80890)
295: return INACCURATE_MATCH;
296: }
297:
298: // verify each parameter
299: for (int i = 0; i < parameterCount; i++) {
300: TypeBinding argType = method.parameters[i];
301: int newLevel = IMPOSSIBLE_MATCH;
302: if (argType.isMemberType()) {
303: // only compare source name for member type (bug 41018)
304: newLevel = CharOperation.match(
305: this .pattern.parameterSimpleNames[i],
306: argType.sourceName(), this .isCaseSensitive) ? ACCURATE_MATCH
307: : IMPOSSIBLE_MATCH;
308: } else {
309: // TODO (frederic) use this call to refine accuracy on parameter types
310: // newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], this.pattern.parametersTypeArguments[i], 0, argType);
311: newLevel = resolveLevelForType(
312: this .pattern.parameterSimpleNames[i],
313: this .pattern.parameterQualifications[i],
314: argType);
315: }
316: if (level > newLevel) {
317: if (newLevel == IMPOSSIBLE_MATCH) {
318: if (skipImpossibleArg) {
319: // Do not consider match as impossible while finding declarations and source level >= 1.5
320: // (see bugs https://bugs.eclipse.org/bugs/show_bug.cgi?id=79990, 96761, 96763)
321: newLevel = level;
322: } else {
323: return IMPOSSIBLE_MATCH;
324: }
325: }
326: level = newLevel; // can only be downgraded
327: }
328: }
329: }
330:
331: return level;
332: }
333:
334: private boolean matchOverriddenMethod(ReferenceBinding type,
335: MethodBinding method, MethodBinding matchMethod) {
336: if (type == null || this .pattern.selector == null)
337: return false;
338:
339: // matches superclass
340: if (!type.isInterface()
341: && !CharOperation.equals(type.compoundName,
342: TypeConstants.JAVA_LANG_OBJECT)) {
343: ReferenceBinding super Class = type.super class();
344: if (super Class.isParameterizedType()) {
345: MethodBinding[] methods = super Class
346: .getMethods(this .pattern.selector);
347: int length = methods.length;
348: for (int i = 0; i < length; i++) {
349: if (methods[i].areParametersEqual(method)) {
350: if (matchMethod == null) {
351: if (methodParametersEqualsPattern(methods[i]
352: .original()))
353: return true;
354: } else {
355: if (methods[i].original()
356: .areParametersEqual(matchMethod))
357: return true;
358: }
359: }
360: }
361: }
362: if (matchOverriddenMethod(super Class, method, matchMethod)) {
363: return true;
364: }
365: }
366:
367: // matches interfaces
368: ReferenceBinding[] interfaces = type.super Interfaces();
369: if (interfaces == null)
370: return false;
371: int iLength = interfaces.length;
372: for (int i = 0; i < iLength; i++) {
373: if (interfaces[i].isParameterizedType()) {
374: MethodBinding[] methods = interfaces[i]
375: .getMethods(this .pattern.selector);
376: int length = methods.length;
377: for (int j = 0; j < length; j++) {
378: if (methods[j].areParametersEqual(method)) {
379: if (matchMethod == null) {
380: if (methodParametersEqualsPattern(methods[j]
381: .original()))
382: return true;
383: } else {
384: if (methods[j].original()
385: .areParametersEqual(matchMethod))
386: return true;
387: }
388: }
389: }
390: }
391: if (matchOverriddenMethod(interfaces[i], method,
392: matchMethod)) {
393: return true;
394: }
395: }
396: return false;
397: }
398:
399: /**
400: * @see org.eclipse.jdt.internal.core.search.matching.PatternLocator#matchReportReference(org.eclipse.jdt.internal.compiler.ast.ASTNode, org.eclipse.jdt.core.IJavaElement, Binding, int, org.eclipse.jdt.internal.core.search.matching.MatchLocator)
401: */
402: protected void matchReportReference(ASTNode reference,
403: IJavaElement element, Binding elementBinding, int accuracy,
404: MatchLocator locator) throws CoreException {
405: MethodBinding methodBinding = (reference instanceof MessageSend) ? ((MessageSend) reference).binding
406: : ((elementBinding instanceof MethodBinding) ? (MethodBinding) elementBinding
407: : null);
408: if (this .isDeclarationOfReferencedMethodsPattern) {
409: if (methodBinding == null)
410: return;
411: // need exact match to be able to open on type ref
412: if (accuracy != SearchMatch.A_ACCURATE)
413: return;
414:
415: // element that references the method must be included in the enclosing element
416: DeclarationOfReferencedMethodsPattern declPattern = (DeclarationOfReferencedMethodsPattern) this .pattern;
417: while (element != null
418: && !declPattern.enclosingElement.equals(element))
419: element = element.getParent();
420: if (element != null) {
421: reportDeclaration(methodBinding, locator,
422: declPattern.knownMethods);
423: }
424: } else {
425: match = locator.newMethodReferenceMatch(element,
426: elementBinding, accuracy, -1, -1,
427: false /*not constructor*/,
428: false/*not synthetic*/, reference);
429: if (this .pattern.findReferences
430: && reference instanceof MessageSend) {
431: IJavaElement focus = ((InternalSearchPattern) this .pattern).focus;
432: // verify closest match if pattern was bound
433: // (see bug 70827)
434: if (focus != null
435: && focus.getElementType() == IJavaElement.METHOD) {
436: if (methodBinding != null
437: && methodBinding.declaringClass != null) {
438: boolean isPrivate = Flags
439: .isPrivate(((IMethod) focus).getFlags());
440: if (isPrivate
441: && !CharOperation
442: .equals(
443: methodBinding.declaringClass.sourceName,
444: focus
445: .getParent()
446: .getElementName()
447: .toCharArray())) {
448: return; // finally the match was not possible
449: }
450: }
451: }
452: matchReportReference((MessageSend) reference, locator,
453: ((MessageSend) reference).binding);
454: } else {
455: if (reference instanceof SingleMemberAnnotation) {
456: reference = ((SingleMemberAnnotation) reference)
457: .memberValuePairs()[0];
458: match.setImplicit(true);
459: }
460: int offset = reference.sourceStart;
461: int length = reference.sourceEnd - offset + 1;
462: match.setOffset(offset);
463: match.setLength(length);
464: locator.report(match);
465: }
466: }
467: }
468:
469: void matchReportReference(MessageSend messageSend,
470: MatchLocator locator, MethodBinding methodBinding)
471: throws CoreException {
472:
473: // Look if there's a need to special report for parameterized type
474: boolean isParameterized = false;
475: if (methodBinding instanceof ParameterizedGenericMethodBinding) { // parameterized generic method
476: isParameterized = true;
477:
478: // Update match regarding method type arguments
479: ParameterizedGenericMethodBinding parameterizedMethodBinding = (ParameterizedGenericMethodBinding) methodBinding;
480: match.setRaw(parameterizedMethodBinding.isRaw);
481: TypeBinding[] typeArguments = /*parameterizedMethodBinding.isRaw ? null :*/parameterizedMethodBinding.typeArguments;
482: updateMatch(typeArguments, locator,
483: this .pattern.methodArguments, this .pattern
484: .hasMethodParameters());
485:
486: // Update match regarding declaring class type arguments
487: if (methodBinding.declaringClass.isParameterizedType()
488: || methodBinding.declaringClass.isRawType()) {
489: ParameterizedTypeBinding parameterizedBinding = (ParameterizedTypeBinding) methodBinding.declaringClass;
490: if (!this .pattern.hasTypeArguments()
491: && this .pattern.hasMethodArguments()
492: || parameterizedBinding
493: .isParameterizedWithOwnVariables()) {
494: // special case for pattern which defines method arguments but not its declaring type
495: // in this case, we do not refine accuracy using declaring type arguments...!
496: } else {
497: updateMatch(parameterizedBinding, this .pattern
498: .getTypeArguments(), this .pattern
499: .hasTypeParameters(), 0, locator);
500: }
501: } else if (this .pattern.hasTypeArguments()) {
502: match.setRule(SearchPattern.R_ERASURE_MATCH);
503: }
504:
505: // Update match regarding method parameters
506: // TODO ? (frederic)
507:
508: // Update match regarding method return type
509: // TODO ? (frederic)
510:
511: // Special case for errors
512: if (match.getRule() != 0
513: && messageSend.resolvedType == null) {
514: match.setRule(SearchPattern.R_ERASURE_MATCH);
515: }
516: } else if (methodBinding instanceof ParameterizedMethodBinding) {
517: isParameterized = true;
518: if (methodBinding.declaringClass.isParameterizedType()
519: || methodBinding.declaringClass.isRawType()) {
520: ParameterizedTypeBinding parameterizedBinding = (ParameterizedTypeBinding) methodBinding.declaringClass;
521: if (!parameterizedBinding
522: .isParameterizedWithOwnVariables()) {
523: updateMatch(parameterizedBinding, this .pattern
524: .getTypeArguments(), this .pattern
525: .hasTypeParameters(), 0, locator);
526: }
527: } else if (this .pattern.hasTypeArguments()) {
528: match.setRule(SearchPattern.R_ERASURE_MATCH);
529: }
530:
531: // Update match regarding method parameters
532: // TODO ? (frederic)
533:
534: // Update match regarding method return type
535: // TODO ? (frederic)
536:
537: // Special case for errors
538: if (match.getRule() != 0
539: && messageSend.resolvedType == null) {
540: match.setRule(SearchPattern.R_ERASURE_MATCH);
541: }
542: } else if (this .pattern.hasMethodArguments()) { // binding has no type params, compatible erasure if pattern does
543: match.setRule(SearchPattern.R_ERASURE_MATCH);
544: }
545:
546: // See whether it is necessary to report or not
547: if (match.getRule() == 0)
548: return; // impossible match
549: boolean report = (this .isErasureMatch && match.isErasure())
550: || (this .isEquivalentMatch && match.isEquivalent())
551: || match.isExact();
552: if (!report)
553: return;
554:
555: // Report match
556: int offset = (int) (messageSend.nameSourcePosition >>> 32);
557: match.setOffset(offset);
558: match.setLength(messageSend.sourceEnd - offset + 1);
559: if (isParameterized && this .pattern.hasMethodArguments()) {
560: locator.reportAccurateParameterizedMethodReference(match,
561: messageSend, messageSend.typeArguments);
562: } else {
563: locator.report(match);
564: }
565: }
566:
567: /*
568: * Return whether method parameters are equals to pattern ones.
569: */
570: private boolean methodParametersEqualsPattern(MethodBinding method) {
571: TypeBinding[] methodParameters = method.parameters;
572:
573: int length = methodParameters.length;
574: if (length != this .pattern.parameterSimpleNames.length)
575: return false;
576:
577: for (int i = 0; i < length; i++) {
578: char[] paramQualifiedName = qualifiedPattern(
579: this .pattern.parameterSimpleNames[i],
580: this .pattern.parameterQualifications[i]);
581: if (!CharOperation.match(paramQualifiedName,
582: methodParameters[i].readableName(),
583: this .isCaseSensitive)) {
584: return false;
585: }
586: }
587: return true;
588: }
589:
590: public SearchMatch newDeclarationMatch(ASTNode reference,
591: IJavaElement element, Binding elementBinding, int accuracy,
592: int length, MatchLocator locator) {
593: if (elementBinding != null) {
594: MethodBinding methodBinding = (MethodBinding) elementBinding;
595: // If method parameters verification was not valid, then try to see if method arguments can match a method in hierarchy
596: if (this .methodDeclarationsWithInvalidParam
597: .containsKey(reference)) {
598: // First see if this reference has already been resolved => report match if validated
599: Boolean report = (Boolean) this .methodDeclarationsWithInvalidParam
600: .get(reference);
601: if (report != null) {
602: if (report.booleanValue()) {
603: return super .newDeclarationMatch(reference,
604: element, elementBinding, accuracy,
605: length, locator);
606: }
607: return null;
608: }
609: if (matchOverriddenMethod(methodBinding.declaringClass,
610: methodBinding, null)) {
611: this .methodDeclarationsWithInvalidParam.put(
612: reference, Boolean.TRUE);
613: return super .newDeclarationMatch(reference,
614: element, elementBinding, accuracy, length,
615: locator);
616: }
617: if (isTypeInSuperDeclaringTypeNames(methodBinding.declaringClass.compoundName)) {
618: MethodBinding patternBinding = locator
619: .getMethodBinding(this .pattern);
620: if (patternBinding != null) {
621: if (!matchOverriddenMethod(
622: patternBinding.declaringClass,
623: patternBinding, methodBinding)) {
624: this .methodDeclarationsWithInvalidParam
625: .put(reference, Boolean.FALSE);
626: return null;
627: }
628: }
629: this .methodDeclarationsWithInvalidParam.put(
630: reference, Boolean.TRUE);
631: return super .newDeclarationMatch(reference,
632: element, elementBinding, accuracy, length,
633: locator);
634: }
635: this .methodDeclarationsWithInvalidParam.put(reference,
636: Boolean.FALSE);
637: return null;
638: }
639: }
640: return super .newDeclarationMatch(reference, element,
641: elementBinding, accuracy, length, locator);
642: }
643:
644: protected int referenceType() {
645: return IJavaElement.METHOD;
646: }
647:
648: protected void reportDeclaration(MethodBinding methodBinding,
649: MatchLocator locator, SimpleSet knownMethods)
650: throws CoreException {
651: ReferenceBinding declaringClass = methodBinding.declaringClass;
652: IType type = locator.lookupType(declaringClass);
653: if (type == null)
654: return; // case of a secondary type
655:
656: char[] bindingSelector = methodBinding.selector;
657: boolean isBinary = type.isBinary();
658: IMethod method = null;
659: TypeBinding[] parameters = methodBinding.original().parameters;
660: int parameterLength = parameters.length;
661: if (isBinary) {
662: char[][] parameterTypes = new char[parameterLength][];
663: for (int i = 0; i < parameterLength; i++) {
664: char[] typeName = parameters[i].qualifiedSourceName();
665: for (int j = 0, dim = parameters[i].dimensions(); j < dim; j++) {
666: typeName = CharOperation.concat(typeName,
667: new char[] { '[', ']' });
668: }
669: parameterTypes[i] = typeName;
670: }
671: method = locator.createBinaryMethodHandle(type,
672: methodBinding.selector, parameterTypes);
673: } else {
674: String[] parameterTypes = new String[parameterLength];
675: for (int i = 0; i < parameterLength; i++) {
676: char[] typeName = parameters[i].shortReadableName();
677: if (parameters[i].isMemberType()) {
678: typeName = CharOperation.subarray(typeName,
679: CharOperation.indexOf('.', typeName) + 1,
680: typeName.length);
681: }
682: parameterTypes[i] = Signature.createTypeSignature(
683: typeName, false);
684: }
685: method = type.getMethod(new String(bindingSelector),
686: parameterTypes);
687: }
688: if (method == null
689: || knownMethods.addIfNotIncluded(method) == null)
690: return;
691:
692: IResource resource = type.getResource();
693: IBinaryType info = null;
694: if (isBinary) {
695: if (resource == null)
696: resource = type.getJavaProject().getProject();
697: info = locator.getBinaryInfo(
698: (org.eclipse.jdt.internal.core.ClassFile) type
699: .getClassFile(), resource);
700: locator.reportBinaryMemberDeclaration(resource, method,
701: methodBinding, info, SearchMatch.A_ACCURATE);
702: } else {
703: if (declaringClass instanceof ParameterizedTypeBinding)
704: declaringClass = ((ParameterizedTypeBinding) declaringClass)
705: .genericType();
706: ClassScope scope = ((SourceTypeBinding) declaringClass).scope;
707: if (scope != null) {
708: TypeDeclaration typeDecl = scope.referenceContext;
709: AbstractMethodDeclaration methodDecl = null;
710: AbstractMethodDeclaration[] methodDecls = typeDecl.methods;
711: for (int i = 0, length = methodDecls.length; i < length; i++) {
712: if (CharOperation.equals(bindingSelector,
713: methodDecls[i].selector)) {
714: methodDecl = methodDecls[i];
715: break;
716: }
717: }
718: if (methodDecl != null) {
719: int offset = methodDecl.sourceStart;
720: Binding binding = methodDecl.binding;
721: if (binding != null)
722: method = (IMethod) ((JavaElement) method)
723: .resolved(binding);
724: match = new MethodDeclarationMatch(method,
725: SearchMatch.A_ACCURATE, offset,
726: methodDecl.sourceEnd - offset + 1, locator
727: .getParticipant(), resource);
728: locator.report(match);
729: }
730: }
731: }
732: }
733:
734: public int resolveLevel(ASTNode possibleMatchingNode) {
735: if (this .pattern.findReferences) {
736: if (possibleMatchingNode instanceof MessageSend) {
737: return resolveLevel((MessageSend) possibleMatchingNode);
738: }
739: if (possibleMatchingNode instanceof SingleMemberAnnotation) {
740: SingleMemberAnnotation annotation = (SingleMemberAnnotation) possibleMatchingNode;
741: return resolveLevel(annotation.memberValuePairs()[0].binding);
742: }
743: if (possibleMatchingNode instanceof MemberValuePair) {
744: MemberValuePair memberValuePair = (MemberValuePair) possibleMatchingNode;
745: return resolveLevel(memberValuePair.binding);
746: }
747: }
748: if (this .pattern.findDeclarations) {
749: if (possibleMatchingNode instanceof MethodDeclaration) {
750: return resolveLevel(((MethodDeclaration) possibleMatchingNode).binding);
751: }
752: }
753: return IMPOSSIBLE_MATCH;
754: }
755:
756: public int resolveLevel(Binding binding) {
757: if (binding == null)
758: return INACCURATE_MATCH;
759: if (!(binding instanceof MethodBinding))
760: return IMPOSSIBLE_MATCH;
761:
762: MethodBinding method = (MethodBinding) binding;
763: boolean skipVerif = this .pattern.findDeclarations
764: && this .mayBeGeneric;
765: int methodLevel = matchMethod(method, skipVerif);
766: if (methodLevel == IMPOSSIBLE_MATCH) {
767: if (method != method.original())
768: methodLevel = matchMethod(method.original(), skipVerif);
769: if (methodLevel == IMPOSSIBLE_MATCH) {
770: return IMPOSSIBLE_MATCH;
771: } else {
772: method = method.original();
773: }
774: }
775:
776: // declaring type
777: char[] qualifiedPattern = qualifiedPattern(
778: this .pattern.declaringSimpleName,
779: this .pattern.declaringQualification);
780: if (qualifiedPattern == null)
781: return methodLevel; // since any declaring class will do
782:
783: boolean subType = !method.isStatic() && !method.isPrivate();
784: if (subType && this .pattern.declaringQualification != null
785: && method.declaringClass != null
786: && method.declaringClass.fPackage != null) {
787: subType = CharOperation.compareWith(
788: this .pattern.declaringQualification,
789: method.declaringClass.fPackage.shortReadableName()) == 0;
790: }
791: int declaringLevel = subType ? resolveLevelAsSubtype(
792: qualifiedPattern, method.declaringClass, null)
793: : resolveLevelForType(qualifiedPattern,
794: method.declaringClass);
795: return methodLevel > declaringLevel ? declaringLevel
796: : methodLevel; // return the weaker match
797: }
798:
799: protected int resolveLevel(MessageSend messageSend) {
800: MethodBinding method = messageSend.binding;
801: if (method == null) {
802: return INACCURATE_MATCH;
803: }
804: if (messageSend.resolvedType == null) {
805: // Closest match may have different argument numbers when ProblemReason is NotFound
806: // see MessageSend#resolveType(BlockScope)
807: // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=97322
808: int argLength = messageSend.arguments == null ? 0
809: : messageSend.arguments.length;
810: if (pattern.parameterSimpleNames == null
811: || argLength == pattern.parameterSimpleNames.length) {
812: return INACCURATE_MATCH;
813: }
814: return IMPOSSIBLE_MATCH;
815: }
816:
817: int methodLevel = matchMethod(method, false);
818: if (methodLevel == IMPOSSIBLE_MATCH) {
819: if (method != method.original())
820: methodLevel = matchMethod(method.original(), false);
821: if (methodLevel == IMPOSSIBLE_MATCH)
822: return IMPOSSIBLE_MATCH;
823: method = method.original();
824: }
825:
826: // receiver type
827: char[] qualifiedPattern = qualifiedPattern(
828: this .pattern.declaringSimpleName,
829: this .pattern.declaringQualification);
830: if (qualifiedPattern == null)
831: return methodLevel; // since any declaring class will do
832:
833: int declaringLevel;
834: if (isVirtualInvoke(method, messageSend)
835: && (messageSend.actualReceiverType instanceof ReferenceBinding)) {
836: ReferenceBinding methodReceiverType = (ReferenceBinding) messageSend.actualReceiverType;
837: declaringLevel = resolveLevelAsSubtype(qualifiedPattern,
838: methodReceiverType, method.parameters);
839: if (declaringLevel == IMPOSSIBLE_MATCH) {
840: if (method.declaringClass == null
841: || this .allSuperDeclaringTypeNames == null) {
842: declaringLevel = INACCURATE_MATCH;
843: } else {
844: char[][] compoundName = methodReceiverType.compoundName;
845: for (int i = 0, max = this .allSuperDeclaringTypeNames.length; i < max; i++) {
846: if (CharOperation.equals(
847: this .allSuperDeclaringTypeNames[i],
848: compoundName)) {
849: return methodLevel // since this is an ACCURATE_MATCH so return the possibly weaker match
850: | SUPER_INVOCATION_FLAVOR; // this is an overridden method => add flavor to returned level
851: }
852: }
853: /* Do not return interfaces potential matches
854: * @see "https://bugs.eclipse.org/bugs/show_bug.cgi?id=157814#c8"
855: if (methodReceiverType.isInterface()) {
856: // all methods interface with same name and parameters are potential matches
857: // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=156491
858: return INACCURATE_MATCH | POLYMORPHIC_FLAVOR;
859: }
860: */
861: }
862: }
863: if ((declaringLevel & FLAVORS_MASK) != 0) {
864: // level got some flavors => return it
865: return declaringLevel;
866: }
867: } else {
868: declaringLevel = resolveLevelForType(qualifiedPattern,
869: method.declaringClass);
870: }
871: return methodLevel > declaringLevel ? declaringLevel
872: : methodLevel; // return the weaker match
873: }
874:
875: /**
876: * Returns whether the given reference type binding matches or is a subtype of a type
877: * that matches the given qualified pattern.
878: * Returns ACCURATE_MATCH if it does.
879: * Returns INACCURATE_MATCH if resolve fails
880: * Returns IMPOSSIBLE_MATCH if it doesn't.
881: */
882: protected int resolveLevelAsSubtype(char[] qualifiedPattern,
883: ReferenceBinding type, TypeBinding[] argumentTypes) {
884: if (type == null)
885: return INACCURATE_MATCH;
886:
887: int level = resolveLevelForType(qualifiedPattern, type);
888: if (level != IMPOSSIBLE_MATCH) {
889: if (!type.isAbstract() && !type.isInterface()) { // if concrete class, then method is overridden
890: level |= OVERRIDDEN_METHOD_FLAVOR;
891: }
892: return level;
893: }
894:
895: // matches superclass
896: if (!type.isInterface()
897: && !CharOperation.equals(type.compoundName,
898: TypeConstants.JAVA_LANG_OBJECT)) {
899: level = resolveLevelAsSubtype(qualifiedPattern, type
900: .super class(), argumentTypes);
901: if (level != IMPOSSIBLE_MATCH) {
902: if (argumentTypes != null) {
903: // need to verify if method may be overridden
904: MethodBinding[] methods = type
905: .getMethods(this .pattern.selector);
906: for (int i = 0, length = methods.length; i < length; i++) {
907: MethodBinding method = methods[i];
908: TypeBinding[] parameters = method.parameters;
909: if (argumentTypes.length == parameters.length) {
910: boolean found = true;
911: for (int j = 0, l = parameters.length; j < l; j++) {
912: if (parameters[j].erasure() != argumentTypes[j]
913: .erasure()) {
914: found = false;
915: break;
916: }
917: }
918: if (found) { // one method match in hierarchy
919: if ((level & OVERRIDDEN_METHOD_FLAVOR) != 0) {
920: // this method is already overridden on a super class, current match is impossible
921: return IMPOSSIBLE_MATCH;
922: }
923: if (!method.isAbstract()
924: && !type.isInterface()) {
925: // store the fact that the method is overridden
926: level |= OVERRIDDEN_METHOD_FLAVOR;
927: }
928: }
929: }
930: }
931: }
932: return level | SUB_INVOCATION_FLAVOR; // add flavor to returned level
933: }
934: }
935:
936: // matches interfaces
937: ReferenceBinding[] interfaces = type.super Interfaces();
938: if (interfaces == null)
939: return INACCURATE_MATCH;
940: for (int i = 0; i < interfaces.length; i++) {
941: level = resolveLevelAsSubtype(qualifiedPattern,
942: interfaces[i], null);
943: if (level != IMPOSSIBLE_MATCH) {
944: if (!type.isAbstract() && !type.isInterface()) { // if concrete class, then method is overridden
945: level |= OVERRIDDEN_METHOD_FLAVOR;
946: }
947: return level | SUB_INVOCATION_FLAVOR; // add flavor to returned level
948: }
949: }
950: return IMPOSSIBLE_MATCH;
951: }
952:
953: public String toString() {
954: return "Locator for " + this .pattern.toString(); //$NON-NLS-1$
955: }
956: }
|