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 org.eclipse.core.runtime.CoreException;
013: import org.eclipse.jdt.core.IJavaElement;
014: import org.eclipse.jdt.core.compiler.CharOperation;
015: import org.eclipse.jdt.core.search.SearchMatch;
016: import org.eclipse.jdt.core.search.SearchPattern;
017: import org.eclipse.jdt.internal.compiler.ast.*;
018: import org.eclipse.jdt.internal.compiler.lookup.Binding;
019: import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
020: import org.eclipse.jdt.internal.compiler.lookup.ParameterizedGenericMethodBinding;
021: import org.eclipse.jdt.internal.compiler.lookup.ParameterizedMethodBinding;
022: import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
023: import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
024:
025: public class ConstructorLocator extends PatternLocator {
026:
027: protected ConstructorPattern pattern;
028:
029: public ConstructorLocator(ConstructorPattern pattern) {
030: super (pattern);
031:
032: this .pattern = pattern;
033: }
034:
035: public int match(ASTNode node, MatchingNodeSet nodeSet) { // interested in ExplicitConstructorCall
036: if (!this .pattern.findReferences)
037: return IMPOSSIBLE_MATCH;
038: if (!(node instanceof ExplicitConstructorCall))
039: return IMPOSSIBLE_MATCH;
040:
041: if (!matchParametersCount(node,
042: ((ExplicitConstructorCall) node).arguments))
043: return IMPOSSIBLE_MATCH;
044:
045: return nodeSet
046: .addMatch(
047: node,
048: ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
049: : ACCURATE_MATCH);
050: }
051:
052: public int match(ConstructorDeclaration node,
053: MatchingNodeSet nodeSet) {
054: int referencesLevel = this .pattern.findReferences ? matchLevelForReferences(node)
055: : IMPOSSIBLE_MATCH;
056: int declarationsLevel = this .pattern.findDeclarations ? matchLevelForDeclarations(node)
057: : IMPOSSIBLE_MATCH;
058:
059: return nodeSet.addMatch(node,
060: referencesLevel >= declarationsLevel ? referencesLevel
061: : declarationsLevel); // use the stronger match
062: }
063:
064: public int match(Expression node, MatchingNodeSet nodeSet) { // interested in AllocationExpression
065: if (!this .pattern.findReferences)
066: return IMPOSSIBLE_MATCH;
067: if (!(node instanceof AllocationExpression))
068: return IMPOSSIBLE_MATCH;
069:
070: // constructor name is simple type name
071: AllocationExpression allocation = (AllocationExpression) node;
072: char[][] typeName = allocation.type.getTypeName();
073: if (this .pattern.declaringSimpleName != null
074: && !matchesName(this .pattern.declaringSimpleName,
075: typeName[typeName.length - 1]))
076: return IMPOSSIBLE_MATCH;
077:
078: if (!matchParametersCount(node, allocation.arguments))
079: return IMPOSSIBLE_MATCH;
080:
081: return nodeSet
082: .addMatch(
083: node,
084: ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
085: : ACCURATE_MATCH);
086: }
087:
088: public int match(FieldDeclaration field, MatchingNodeSet nodeSet) {
089: if (!this .pattern.findReferences)
090: return IMPOSSIBLE_MATCH;
091: // look only for enum constant
092: if (field.type != null
093: || !(field.initialization instanceof AllocationExpression))
094: return IMPOSSIBLE_MATCH;
095:
096: AllocationExpression allocation = (AllocationExpression) field.initialization;
097: if (field.binding != null
098: && field.binding.declaringClass != null) {
099: if (this .pattern.declaringSimpleName != null
100: && !matchesName(this .pattern.declaringSimpleName,
101: field.binding.declaringClass.sourceName()))
102: return IMPOSSIBLE_MATCH;
103: }
104:
105: if (!matchParametersCount(field, allocation.arguments))
106: return IMPOSSIBLE_MATCH;
107:
108: return nodeSet
109: .addMatch(
110: field,
111: ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
112: : ACCURATE_MATCH);
113: }
114:
115: //public int match(MethodDeclaration node, MatchingNodeSet nodeSet) - SKIP IT
116: /**
117: * Special case for message send in javadoc comment. They can be in fact bound to a contructor.
118: * @see "http://bugs.eclipse.org/bugs/show_bug.cgi?id=83285"
119: */
120: public int match(MessageSend msgSend, MatchingNodeSet nodeSet) {
121: if ((msgSend.bits & ASTNode.InsideJavadoc) == 0)
122: return IMPOSSIBLE_MATCH;
123: if (this .pattern.declaringSimpleName == null
124: || CharOperation.equals(msgSend.selector,
125: this .pattern.declaringSimpleName)) {
126: return nodeSet
127: .addMatch(
128: msgSend,
129: ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
130: : ACCURATE_MATCH);
131: }
132: return IMPOSSIBLE_MATCH;
133: }
134:
135: //public int match(Reference node, MatchingNodeSet nodeSet) - SKIP IT
136: public int match(TypeDeclaration node, MatchingNodeSet nodeSet) {
137: if (!this .pattern.findReferences)
138: return IMPOSSIBLE_MATCH;
139:
140: // need to look for a generated default constructor
141: return nodeSet
142: .addMatch(
143: node,
144: ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
145: : ACCURATE_MATCH);
146: }
147:
148: //public int match(TypeReference node, MatchingNodeSet nodeSet) - SKIP IT
149:
150: protected int matchConstructor(MethodBinding constructor) {
151: if (!constructor.isConstructor())
152: return IMPOSSIBLE_MATCH;
153:
154: // declaring type, simple name has already been matched by matchIndexEntry()
155: int level = resolveLevelForType(
156: this .pattern.declaringSimpleName,
157: this .pattern.declaringQualification,
158: constructor.declaringClass);
159: if (level == IMPOSSIBLE_MATCH)
160: return IMPOSSIBLE_MATCH;
161:
162: // parameter types
163: int parameterCount = this .pattern.parameterCount;
164: if (parameterCount > -1) {
165: if (constructor.parameters == null)
166: return INACCURATE_MATCH;
167: if (parameterCount != constructor.parameters.length)
168: return IMPOSSIBLE_MATCH;
169: for (int i = 0; i < parameterCount; i++) {
170: // TODO (frederic) use this call to refine accuracy on parameter types
171: // int newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], this.pattern.parametersTypeArguments[i], 0, constructor.parameters[i]);
172: int newLevel = resolveLevelForType(
173: this .pattern.parameterSimpleNames[i],
174: this .pattern.parameterQualifications[i],
175: constructor.parameters[i]);
176: if (level > newLevel) {
177: if (newLevel == IMPOSSIBLE_MATCH) {
178: // if (isErasureMatch) {
179: // return ERASURE_MATCH;
180: // }
181: return IMPOSSIBLE_MATCH;
182: }
183: level = newLevel; // can only be downgraded
184: }
185: }
186: }
187: return level;
188: }
189:
190: protected int matchContainer() {
191: if (this .pattern.findReferences)
192: return ALL_CONTAINER; // handles both declarations + references & just references
193: // COMPILATION_UNIT_CONTAINER - implicit constructor call: case of Y extends X and Y doesn't define any constructor
194: // CLASS_CONTAINER - implicit constructor call: case of constructor declaration with no explicit super call
195: // METHOD_CONTAINER - reference in another constructor
196: // FIELD_CONTAINER - anonymous in a field initializer
197:
198: // declarations are only found in Class
199: return CLASS_CONTAINER;
200: }
201:
202: protected int matchLevelForReferences(
203: ConstructorDeclaration constructor) {
204: ExplicitConstructorCall constructorCall = constructor.constructorCall;
205: if (constructorCall == null
206: || constructorCall.accessMode != ExplicitConstructorCall.ImplicitSuper)
207: return IMPOSSIBLE_MATCH;
208:
209: if (this .pattern.parameterSimpleNames != null) {
210: int length = this .pattern.parameterSimpleNames.length;
211: Expression[] args = constructorCall.arguments;
212: int argsLength = args == null ? 0 : args.length;
213: if (length != argsLength)
214: return IMPOSSIBLE_MATCH;
215: }
216: return ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
217: : ACCURATE_MATCH;
218: }
219:
220: protected int matchLevelForDeclarations(
221: ConstructorDeclaration constructor) {
222: // constructor name is stored in selector field
223: if (this .pattern.declaringSimpleName != null
224: && !matchesName(this .pattern.declaringSimpleName,
225: constructor.selector))
226: return IMPOSSIBLE_MATCH;
227:
228: if (this .pattern.parameterSimpleNames != null) {
229: int length = this .pattern.parameterSimpleNames.length;
230: Argument[] args = constructor.arguments;
231: int argsLength = args == null ? 0 : args.length;
232: if (length != argsLength)
233: return IMPOSSIBLE_MATCH;
234: }
235:
236: // Verify type arguments (do not reject if pattern has no argument as it can be an erasure match)
237: if (this .pattern.hasConstructorArguments()) {
238: if (constructor.typeParameters == null
239: || constructor.typeParameters.length != this .pattern.constructorArguments.length)
240: return IMPOSSIBLE_MATCH;
241: }
242:
243: return ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
244: : ACCURATE_MATCH;
245: }
246:
247: boolean matchParametersCount(ASTNode node, Expression[] args) {
248: if (this .pattern.parameterSimpleNames != null
249: && (!this .pattern.varargs || ((node.bits & ASTNode.InsideJavadoc) != 0))) {
250: int length = this .pattern.parameterCount;
251: if (length < 0)
252: length = this .pattern.parameterSimpleNames.length;
253: int argsLength = args == null ? 0 : args.length;
254: if (length != argsLength) {
255: return false;
256: }
257: }
258: return true;
259: }
260:
261: protected void matchReportReference(ASTNode reference,
262: IJavaElement element, Binding elementBinding, int accuracy,
263: MatchLocator locator) throws CoreException {
264:
265: MethodBinding constructorBinding = null;
266: boolean isSynthetic = false;
267: if (reference instanceof ExplicitConstructorCall) {
268: ExplicitConstructorCall call = (ExplicitConstructorCall) reference;
269: isSynthetic = call.isImplicitSuper();
270: constructorBinding = call.binding;
271: } else if (reference instanceof AllocationExpression) {
272: AllocationExpression alloc = (AllocationExpression) reference;
273: constructorBinding = alloc.binding;
274: } else if (reference instanceof TypeDeclaration
275: || reference instanceof FieldDeclaration) {
276: super .matchReportReference(reference, element,
277: elementBinding, accuracy, locator);
278: if (match != null)
279: return;
280: }
281:
282: // Create search match
283: match = locator.newMethodReferenceMatch(element,
284: elementBinding, accuracy, -1, -1, true, isSynthetic,
285: reference);
286:
287: // Look to refine accuracy
288: if (constructorBinding instanceof ParameterizedGenericMethodBinding) { // parameterized generic method
289: // Update match regarding constructor type arguments
290: ParameterizedGenericMethodBinding parameterizedMethodBinding = (ParameterizedGenericMethodBinding) constructorBinding;
291: match.setRaw(parameterizedMethodBinding.isRaw);
292: TypeBinding[] typeBindings = parameterizedMethodBinding.isRaw ? null
293: : parameterizedMethodBinding.typeArguments;
294: updateMatch(typeBindings, locator,
295: this .pattern.constructorArguments, this .pattern
296: .hasConstructorParameters());
297:
298: // Update match regarding declaring class type arguments
299: if (constructorBinding.declaringClass.isParameterizedType()
300: || constructorBinding.declaringClass.isRawType()) {
301: ParameterizedTypeBinding parameterizedBinding = (ParameterizedTypeBinding) constructorBinding.declaringClass;
302: if (!this .pattern.hasTypeArguments()
303: && this .pattern.hasConstructorArguments()
304: || parameterizedBinding
305: .isParameterizedWithOwnVariables()) {
306: // special case for constructor pattern which defines arguments but no type
307: // in this case, we only use refined accuracy for constructor
308: } else if (this .pattern.hasTypeArguments()
309: && !this .pattern.hasConstructorArguments()) {
310: // special case for constructor pattern which defines no constructor arguments but has type ones
311: // in this case, we do not use refined accuracy
312: updateMatch(parameterizedBinding, this .pattern
313: .getTypeArguments(), this .pattern
314: .hasTypeParameters(), 0, locator);
315: } else {
316: updateMatch(parameterizedBinding, this .pattern
317: .getTypeArguments(), this .pattern
318: .hasTypeParameters(), 0, locator);
319: }
320: } else if (this .pattern.hasTypeArguments()) {
321: match.setRule(SearchPattern.R_ERASURE_MATCH);
322: }
323:
324: // Update match regarding constructor parameters
325: // TODO ? (frederic)
326: } else if (constructorBinding instanceof ParameterizedMethodBinding) {
327: // Update match regarding declaring class type arguments
328: if (constructorBinding.declaringClass.isParameterizedType()
329: || constructorBinding.declaringClass.isRawType()) {
330: ParameterizedTypeBinding parameterizedBinding = (ParameterizedTypeBinding) constructorBinding.declaringClass;
331: if (!this .pattern.hasTypeArguments()
332: && this .pattern.hasConstructorArguments()) {
333: // special case for constructor pattern which defines arguments but no type
334: updateMatch(
335: parameterizedBinding,
336: new char[][][] { this .pattern.constructorArguments },
337: this .pattern.hasTypeParameters(), 0,
338: locator);
339: } else if (!parameterizedBinding
340: .isParameterizedWithOwnVariables()) {
341: updateMatch(parameterizedBinding, this .pattern
342: .getTypeArguments(), this .pattern
343: .hasTypeParameters(), 0, locator);
344: }
345: } else if (this .pattern.hasTypeArguments()) {
346: match.setRule(SearchPattern.R_ERASURE_MATCH);
347: }
348:
349: // Update match regarding constructor parameters
350: // TODO ? (frederic)
351: } else if (this .pattern.hasConstructorArguments()) { // binding has no type params, compatible erasure if pattern does
352: match.setRule(SearchPattern.R_ERASURE_MATCH);
353: }
354:
355: // See whether it is necessary to report or not
356: if (match.getRule() == 0)
357: return; // impossible match
358: boolean report = (this .isErasureMatch && match.isErasure())
359: || (this .isEquivalentMatch && match.isEquivalent())
360: || match.isExact();
361: if (!report)
362: return;
363:
364: // Report match
365: int offset = reference.sourceStart;
366: match.setOffset(offset);
367: match.setLength(reference.sourceEnd - offset + 1);
368: if (reference instanceof FieldDeclaration) { // enum declaration
369: FieldDeclaration enumConstant = (FieldDeclaration) reference;
370: if (enumConstant.initialization instanceof QualifiedAllocationExpression) {
371: locator
372: .reportAccurateEnumConstructorReference(
373: match,
374: enumConstant,
375: (QualifiedAllocationExpression) enumConstant.initialization);
376: return;
377: }
378: }
379: locator.report(match);
380: }
381:
382: public SearchMatch newDeclarationMatch(ASTNode reference,
383: IJavaElement element, Binding binding, int accuracy,
384: int length, MatchLocator locator) {
385: match = null;
386: int offset = reference.sourceStart;
387: if (this .pattern.findReferences) {
388: if (reference instanceof TypeDeclaration) {
389: TypeDeclaration type = (TypeDeclaration) reference;
390: AbstractMethodDeclaration[] methods = type.methods;
391: if (methods != null) {
392: for (int i = 0, max = methods.length; i < max; i++) {
393: AbstractMethodDeclaration method = methods[i];
394: boolean synthetic = method
395: .isDefaultConstructor()
396: && method.sourceStart < type.bodyStart;
397: match = locator.newMethodReferenceMatch(
398: element, binding, accuracy, offset,
399: length, method.isConstructor(),
400: synthetic, method);
401: }
402: }
403: } else if (reference instanceof ConstructorDeclaration) {
404: ConstructorDeclaration constructor = (ConstructorDeclaration) reference;
405: ExplicitConstructorCall call = constructor.constructorCall;
406: boolean synthetic = call != null
407: && call.isImplicitSuper();
408: match = locator.newMethodReferenceMatch(element,
409: binding, accuracy, offset, length, constructor
410: .isConstructor(), synthetic,
411: constructor);
412: }
413: }
414: if (match != null) {
415: return match;
416: }
417: // super implementation...
418: return locator.newDeclarationMatch(element, binding, accuracy,
419: reference.sourceStart, length);
420: }
421:
422: public int resolveLevel(ASTNode node) {
423: if (this .pattern.findReferences) {
424: if (node instanceof AllocationExpression)
425: return resolveLevel((AllocationExpression) node);
426: if (node instanceof ExplicitConstructorCall)
427: return resolveLevel(((ExplicitConstructorCall) node).binding);
428: if (node instanceof TypeDeclaration)
429: return resolveLevel((TypeDeclaration) node);
430: if (node instanceof FieldDeclaration)
431: return resolveLevel((FieldDeclaration) node);
432: if (node instanceof JavadocMessageSend) {
433: return resolveLevel(((JavadocMessageSend) node).binding);
434: }
435: }
436: if (node instanceof ConstructorDeclaration)
437: return resolveLevel((ConstructorDeclaration) node, true);
438: return IMPOSSIBLE_MATCH;
439: }
440:
441: protected int referenceType() {
442: return IJavaElement.METHOD;
443: }
444:
445: protected int resolveLevel(AllocationExpression allocation) {
446: // constructor name is simple type name
447: char[][] typeName = allocation.type.getTypeName();
448: if (this .pattern.declaringSimpleName != null
449: && !matchesName(this .pattern.declaringSimpleName,
450: typeName[typeName.length - 1]))
451: return IMPOSSIBLE_MATCH;
452:
453: return resolveLevel(allocation.binding);
454: }
455:
456: protected int resolveLevel(FieldDeclaration field) {
457: // only accept enum constants
458: if (field.type != null || field.binding == null)
459: return IMPOSSIBLE_MATCH;
460: if (this .pattern.declaringSimpleName != null
461: && !matchesName(this .pattern.declaringSimpleName,
462: field.binding.type.sourceName()))
463: return IMPOSSIBLE_MATCH;
464: if (!(field.initialization instanceof AllocationExpression)
465: || field.initialization.resolvedType.isLocalType())
466: return IMPOSSIBLE_MATCH;
467:
468: return resolveLevel(((AllocationExpression) field.initialization).binding);
469: }
470:
471: public int resolveLevel(Binding binding) {
472: if (binding == null)
473: return INACCURATE_MATCH;
474: if (!(binding instanceof MethodBinding))
475: return IMPOSSIBLE_MATCH;
476:
477: MethodBinding constructor = (MethodBinding) binding;
478: int level = matchConstructor(constructor);
479: if (level == IMPOSSIBLE_MATCH) {
480: if (constructor != constructor.original()) {
481: level = matchConstructor(constructor.original());
482: }
483: }
484: return level;
485: }
486:
487: protected int resolveLevel(ConstructorDeclaration constructor,
488: boolean checkDeclarations) {
489: int referencesLevel = IMPOSSIBLE_MATCH;
490: if (this .pattern.findReferences) {
491: ExplicitConstructorCall constructorCall = constructor.constructorCall;
492: if (constructorCall != null
493: && constructorCall.accessMode == ExplicitConstructorCall.ImplicitSuper) {
494: // eliminate explicit super call as it will be treated with matchLevel(ExplicitConstructorCall, boolean)
495: int callCount = (constructorCall.arguments == null) ? 0
496: : constructorCall.arguments.length;
497: int patternCount = (this .pattern.parameterSimpleNames == null) ? 0
498: : this .pattern.parameterSimpleNames.length;
499: if (patternCount != callCount) {
500: referencesLevel = IMPOSSIBLE_MATCH;
501: } else {
502: referencesLevel = resolveLevel(constructorCall.binding);
503: if (referencesLevel == ACCURATE_MATCH)
504: return ACCURATE_MATCH; // cannot get better
505: }
506: }
507: }
508: if (!checkDeclarations)
509: return referencesLevel;
510:
511: int declarationsLevel = this .pattern.findDeclarations ? resolveLevel(constructor.binding)
512: : IMPOSSIBLE_MATCH;
513: return referencesLevel >= declarationsLevel ? referencesLevel
514: : declarationsLevel; // answer the stronger match
515: }
516:
517: protected int resolveLevel(TypeDeclaration type) {
518: // find default constructor
519: AbstractMethodDeclaration[] methods = type.methods;
520: if (methods != null) {
521: for (int i = 0, length = methods.length; i < length; i++) {
522: AbstractMethodDeclaration method = methods[i];
523: if (method.isDefaultConstructor()
524: && method.sourceStart < type.bodyStart) // if synthetic
525: return resolveLevel(
526: (ConstructorDeclaration) method, false);
527: }
528: }
529: return IMPOSSIBLE_MATCH;
530: }
531:
532: public String toString() {
533: return "Locator for " + this .pattern.toString(); //$NON-NLS-1$
534: }
535: }
|