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.resources.IResource;
013: import org.eclipse.core.runtime.CoreException;
014: import org.eclipse.jdt.core.*;
015: import org.eclipse.jdt.core.compiler.CharOperation;
016: import org.eclipse.jdt.core.search.*;
017: import org.eclipse.jdt.internal.compiler.ast.*;
018: import org.eclipse.jdt.internal.compiler.env.IBinaryType;
019: import org.eclipse.jdt.internal.compiler.lookup.*;
020: import org.eclipse.jdt.internal.compiler.util.SimpleSet;
021: import org.eclipse.jdt.internal.core.JavaElement;
022:
023: public class FieldLocator extends VariableLocator {
024:
025: protected boolean isDeclarationOfAccessedFieldsPattern;
026:
027: public FieldLocator(FieldPattern pattern) {
028: super (pattern);
029:
030: this .isDeclarationOfAccessedFieldsPattern = this .pattern instanceof DeclarationOfAccessedFieldsPattern;
031: }
032:
033: public int match(ASTNode node, MatchingNodeSet nodeSet) {
034: int declarationsLevel = IMPOSSIBLE_MATCH;
035: if (this .pattern.findReferences) {
036: if (node instanceof ImportReference) {
037: // With static import, we can have static field reference in import reference
038: ImportReference importRef = (ImportReference) node;
039: int length = importRef.tokens.length - 1;
040: if (importRef.isStatic()
041: && ((importRef.bits & ASTNode.OnDemand) == 0)
042: && matchesName(this .pattern.name,
043: importRef.tokens[length])) {
044: char[][] compoundName = new char[length][];
045: System.arraycopy(importRef.tokens, 0, compoundName,
046: 0, length);
047: FieldPattern fieldPattern = (FieldPattern) this .pattern;
048: char[] declaringType = CharOperation.concat(
049: fieldPattern.declaringQualification,
050: fieldPattern.declaringSimpleName, '.');
051: if (matchesName(declaringType, CharOperation
052: .concatWith(compoundName, '.'))) {
053: declarationsLevel = ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
054: : ACCURATE_MATCH;
055: }
056: }
057: }
058: }
059: return nodeSet.addMatch(node, declarationsLevel);
060: }
061:
062: //public int match(ConstructorDeclaration node, MatchingNodeSet nodeSet) - SKIP IT
063: public int match(FieldDeclaration node, MatchingNodeSet nodeSet) {
064: int referencesLevel = IMPOSSIBLE_MATCH;
065: if (this .pattern.findReferences)
066: // must be a write only access with an initializer
067: if (this .pattern.writeAccess && !this .pattern.readAccess
068: && node.initialization != null)
069: if (matchesName(this .pattern.name, node.name))
070: referencesLevel = ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
071: : ACCURATE_MATCH;
072:
073: int declarationsLevel = IMPOSSIBLE_MATCH;
074: if (this .pattern.findDeclarations) {
075: switch (node.getKind()) {
076: case AbstractVariableDeclaration.FIELD:
077: case AbstractVariableDeclaration.ENUM_CONSTANT:
078: if (matchesName(this .pattern.name, node.name))
079: if (matchesTypeReference(
080: ((FieldPattern) this .pattern).typeSimpleName,
081: node.type))
082: declarationsLevel = ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
083: : ACCURATE_MATCH;
084: break;
085: }
086: }
087: return nodeSet.addMatch(node,
088: referencesLevel >= declarationsLevel ? referencesLevel
089: : declarationsLevel); // use the stronger match
090: }
091:
092: //public int match(MethodDeclaration node, MatchingNodeSet nodeSet) - SKIP IT
093: //public int match(MessageSend node, MatchingNodeSet nodeSet) - SKIP IT
094: //public int match(TypeDeclaration node, MatchingNodeSet nodeSet) - SKIP IT
095: //public int match(TypeReference node, MatchingNodeSet nodeSet) - SKIP IT
096:
097: protected int matchContainer() {
098: if (this .pattern.findReferences) {
099: // need to look everywhere to find in javadocs and static import
100: return ALL_CONTAINER;
101: }
102: return CLASS_CONTAINER;
103: }
104:
105: protected int matchField(FieldBinding field, boolean matchName) {
106: if (field == null)
107: return INACCURATE_MATCH;
108:
109: if (matchName
110: && !matchesName(this .pattern.name, field.readableName()))
111: return IMPOSSIBLE_MATCH;
112:
113: FieldPattern fieldPattern = (FieldPattern) this .pattern;
114: ReferenceBinding receiverBinding = field.declaringClass;
115: if (receiverBinding == null) {
116: if (field == ArrayBinding.ArrayLength)
117: // optimized case for length field of an array
118: return fieldPattern.declaringQualification == null
119: && fieldPattern.declaringSimpleName == null ? ACCURATE_MATCH
120: : IMPOSSIBLE_MATCH;
121: return INACCURATE_MATCH;
122: }
123:
124: // Note there is no dynamic lookup for field access
125: int declaringLevel = resolveLevelForType(
126: fieldPattern.declaringSimpleName,
127: fieldPattern.declaringQualification, receiverBinding);
128: if (declaringLevel == IMPOSSIBLE_MATCH)
129: return IMPOSSIBLE_MATCH;
130:
131: // look at field type only if declaring type is not specified
132: if (fieldPattern.declaringSimpleName == null)
133: return declaringLevel;
134:
135: // get real field binding
136: FieldBinding fieldBinding = field;
137: if (field instanceof ParameterizedFieldBinding) {
138: fieldBinding = ((ParameterizedFieldBinding) field).originalField;
139: }
140:
141: int typeLevel = resolveLevelForType(fieldBinding.type);
142: return declaringLevel > typeLevel ? typeLevel : declaringLevel; // return the weaker match
143: }
144:
145: /* (non-Javadoc)
146: * @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)
147: * Accept to report match of static field on static import
148: */
149: protected void matchLevelAndReportImportRef(
150: ImportReference importRef, Binding binding,
151: MatchLocator locator) throws CoreException {
152: if (importRef.isStatic() && binding instanceof FieldBinding) {
153: super .matchLevelAndReportImportRef(importRef, binding,
154: locator);
155: }
156: }
157:
158: protected int matchReference(Reference node,
159: MatchingNodeSet nodeSet, boolean writeOnlyAccess) {
160: if (node instanceof FieldReference) {
161: if (matchesName(this .pattern.name,
162: ((FieldReference) node).token))
163: return nodeSet
164: .addMatch(
165: node,
166: ((InternalSearchPattern) this .pattern).mustResolve ? POSSIBLE_MATCH
167: : ACCURATE_MATCH);
168: return IMPOSSIBLE_MATCH;
169: }
170: return super .matchReference(node, nodeSet, writeOnlyAccess);
171: }
172:
173: protected void matchReportReference(ASTNode reference,
174: IJavaElement element, Binding elementBinding, int accuracy,
175: MatchLocator locator) throws CoreException {
176: if (this .isDeclarationOfAccessedFieldsPattern) {
177: // need exact match to be able to open on type ref
178: if (accuracy != SearchMatch.A_ACCURATE)
179: return;
180:
181: // element that references the field must be included in the enclosing element
182: DeclarationOfAccessedFieldsPattern declPattern = (DeclarationOfAccessedFieldsPattern) this .pattern;
183: while (element != null
184: && !declPattern.enclosingElement.equals(element))
185: element = element.getParent();
186: if (element != null) {
187: if (reference instanceof FieldReference) {
188: reportDeclaration(
189: ((FieldReference) reference).binding,
190: locator, declPattern.knownFields);
191: } else if (reference instanceof QualifiedNameReference) {
192: QualifiedNameReference qNameRef = (QualifiedNameReference) reference;
193: Binding nameBinding = qNameRef.binding;
194: if (nameBinding instanceof FieldBinding)
195: reportDeclaration((FieldBinding) nameBinding,
196: locator, declPattern.knownFields);
197: int otherMax = qNameRef.otherBindings == null ? 0
198: : qNameRef.otherBindings.length;
199: for (int i = 0; i < otherMax; i++)
200: reportDeclaration(qNameRef.otherBindings[i],
201: locator, declPattern.knownFields);
202: } else if (reference instanceof SingleNameReference) {
203: reportDeclaration(
204: (FieldBinding) ((SingleNameReference) reference).binding,
205: locator, declPattern.knownFields);
206: }
207: }
208: } else if (reference instanceof ImportReference) {
209: ImportReference importRef = (ImportReference) reference;
210: long[] positions = importRef.sourcePositions;
211: int lastIndex = importRef.tokens.length - 1;
212: int start = (int) ((positions[lastIndex]) >>> 32);
213: int end = (int) positions[lastIndex];
214: match = locator.newFieldReferenceMatch(element,
215: elementBinding, accuracy, start, end - start + 1,
216: importRef);
217: locator.report(match);
218: } else if (reference instanceof FieldReference) {
219: FieldReference fieldReference = (FieldReference) reference;
220: long position = fieldReference.nameSourcePosition;
221: int start = (int) (position >>> 32);
222: int end = (int) position;
223: match = locator.newFieldReferenceMatch(element,
224: elementBinding, accuracy, start, end - start + 1,
225: fieldReference);
226: locator.report(match);
227: } else if (reference instanceof SingleNameReference) {
228: int offset = reference.sourceStart;
229: match = locator.newFieldReferenceMatch(element,
230: elementBinding, accuracy, offset,
231: reference.sourceEnd - offset + 1, reference);
232: locator.report(match);
233: } else if (reference instanceof QualifiedNameReference) {
234: QualifiedNameReference qNameRef = (QualifiedNameReference) reference;
235: int length = qNameRef.tokens.length;
236: SearchMatch[] matches = new SearchMatch[length];
237: Binding nameBinding = qNameRef.binding;
238: int indexOfFirstFieldBinding = qNameRef.indexOfFirstFieldBinding > 0 ? qNameRef.indexOfFirstFieldBinding - 1
239: : 0;
240:
241: // first token
242: if (matchesName(this .pattern.name,
243: qNameRef.tokens[indexOfFirstFieldBinding])
244: && !(nameBinding instanceof LocalVariableBinding)) {
245: FieldBinding fieldBinding = nameBinding instanceof FieldBinding ? (FieldBinding) nameBinding
246: : null;
247: if (fieldBinding == null) {
248: matches[indexOfFirstFieldBinding] = locator
249: .newFieldReferenceMatch(element,
250: elementBinding, accuracy, -1, -1,
251: reference);
252: } else {
253: switch (matchField(fieldBinding, false)) {
254: case ACCURATE_MATCH:
255: matches[indexOfFirstFieldBinding] = locator
256: .newFieldReferenceMatch(element,
257: elementBinding,
258: SearchMatch.A_ACCURATE, -1, -1,
259: reference);
260: break;
261: case INACCURATE_MATCH:
262: match = locator.newFieldReferenceMatch(element,
263: elementBinding,
264: SearchMatch.A_INACCURATE, -1, -1,
265: reference);
266: if (fieldBinding.type != null
267: && fieldBinding.type
268: .isParameterizedType()
269: && this .pattern.hasTypeArguments()) {
270: updateMatch(
271: (ParameterizedTypeBinding) fieldBinding.type,
272: this .pattern.getTypeArguments(),
273: locator);
274: }
275: matches[indexOfFirstFieldBinding] = match;
276: break;
277: }
278: }
279: }
280:
281: // other tokens
282: for (int i = indexOfFirstFieldBinding + 1; i < length; i++) {
283: char[] token = qNameRef.tokens[i];
284: if (matchesName(this .pattern.name, token)) {
285: FieldBinding otherBinding = qNameRef.otherBindings == null ? null
286: : qNameRef.otherBindings[i
287: - (indexOfFirstFieldBinding + 1)];
288: if (otherBinding == null) {
289: matches[i] = locator.newFieldReferenceMatch(
290: element, elementBinding, accuracy, -1,
291: -1, reference);
292: } else {
293: switch (matchField(otherBinding, false)) {
294: case ACCURATE_MATCH:
295: matches[i] = locator
296: .newFieldReferenceMatch(element,
297: elementBinding,
298: SearchMatch.A_ACCURATE, -1,
299: -1, reference);
300: break;
301: case INACCURATE_MATCH:
302: match = locator.newFieldReferenceMatch(
303: element, elementBinding,
304: SearchMatch.A_INACCURATE, -1, -1,
305: reference);
306: if (otherBinding.type != null
307: && otherBinding.type
308: .isParameterizedType()
309: && this .pattern.hasTypeArguments()) {
310: updateMatch(
311: (ParameterizedTypeBinding) otherBinding.type,
312: this .pattern.getTypeArguments(),
313: locator);
314: }
315: matches[i] = match;
316: break;
317: }
318: }
319: }
320: }
321: locator.reportAccurateFieldReference(matches, qNameRef);
322: }
323: }
324:
325: /* (non-Javadoc)
326: * Overridden to reject unexact matches.
327: * @see org.eclipse.jdt.internal.core.search.matching.PatternLocator#updateMatch(org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding, char[][][], org.eclipse.jdt.internal.core.search.matching.MatchLocator)
328: *
329: */
330: protected void updateMatch(
331: ParameterizedTypeBinding parameterizedBinding,
332: char[][][] patternTypeArguments, MatchLocator locator) {
333: // We can only refine if locator has an unit scope.
334: if (locator.unitScope == null)
335: return;
336: updateMatch(parameterizedBinding, patternTypeArguments, false,
337: 0, locator);
338: if (!match.isExact()) {
339: // cannot accept neither erasure nor compatible match
340: match.setRule(0);
341: }
342: }
343:
344: protected void reportDeclaration(FieldBinding fieldBinding,
345: MatchLocator locator, SimpleSet knownFields)
346: throws CoreException {
347: // ignore length field
348: if (fieldBinding == ArrayBinding.ArrayLength)
349: return;
350:
351: ReferenceBinding declaringClass = fieldBinding.declaringClass;
352: IType type = locator.lookupType(declaringClass);
353: if (type == null)
354: return; // case of a secondary type
355:
356: char[] bindingName = fieldBinding.name;
357: IField field = type.getField(new String(bindingName));
358: if (knownFields.addIfNotIncluded(field) == null)
359: return;
360:
361: IResource resource = type.getResource();
362: boolean isBinary = type.isBinary();
363: IBinaryType info = null;
364: if (isBinary) {
365: if (resource == null)
366: resource = type.getJavaProject().getProject();
367: info = locator.getBinaryInfo(
368: (org.eclipse.jdt.internal.core.ClassFile) type
369: .getClassFile(), resource);
370: locator.reportBinaryMemberDeclaration(resource, field,
371: fieldBinding, info, SearchMatch.A_ACCURATE);
372: } else {
373: if (declaringClass instanceof ParameterizedTypeBinding)
374: declaringClass = ((ParameterizedTypeBinding) declaringClass)
375: .genericType();
376: ClassScope scope = ((SourceTypeBinding) declaringClass).scope;
377: if (scope != null) {
378: TypeDeclaration typeDecl = scope.referenceContext;
379: FieldDeclaration fieldDecl = null;
380: FieldDeclaration[] fieldDecls = typeDecl.fields;
381: for (int i = 0, length = fieldDecls.length; i < length; i++) {
382: if (CharOperation.equals(bindingName,
383: fieldDecls[i].name)) {
384: fieldDecl = fieldDecls[i];
385: break;
386: }
387: }
388: if (fieldDecl != null) {
389: int offset = fieldDecl.sourceStart;
390: match = new FieldDeclarationMatch(
391: ((JavaElement) field)
392: .resolved(fieldBinding),
393: SearchMatch.A_ACCURATE, offset,
394: fieldDecl.sourceEnd - offset + 1, locator
395: .getParticipant(), resource);
396: locator.report(match);
397: }
398: }
399: }
400: }
401:
402: protected int referenceType() {
403: return IJavaElement.FIELD;
404: }
405:
406: public int resolveLevel(ASTNode possiblelMatchingNode) {
407: if (this .pattern.findReferences) {
408: if (possiblelMatchingNode instanceof FieldReference)
409: return matchField(
410: ((FieldReference) possiblelMatchingNode).binding,
411: true);
412: else if (possiblelMatchingNode instanceof NameReference)
413: return resolveLevel((NameReference) possiblelMatchingNode);
414: }
415: if (possiblelMatchingNode instanceof FieldDeclaration)
416: return matchField(
417: ((FieldDeclaration) possiblelMatchingNode).binding,
418: true);
419: return IMPOSSIBLE_MATCH;
420: }
421:
422: public int resolveLevel(Binding binding) {
423: if (binding == null)
424: return INACCURATE_MATCH;
425: if (!(binding instanceof FieldBinding))
426: return IMPOSSIBLE_MATCH;
427:
428: return matchField((FieldBinding) binding, true);
429: }
430:
431: protected int resolveLevel(NameReference nameRef) {
432: if (nameRef instanceof SingleNameReference)
433: return resolveLevel(nameRef.binding);
434:
435: Binding binding = nameRef.binding;
436: QualifiedNameReference qNameRef = (QualifiedNameReference) nameRef;
437: FieldBinding fieldBinding = null;
438: if (binding instanceof FieldBinding) {
439: fieldBinding = (FieldBinding) binding;
440: char[] bindingName = fieldBinding.name;
441: int lastDot = CharOperation.lastIndexOf('.', bindingName);
442: if (lastDot > -1)
443: bindingName = CharOperation.subarray(bindingName,
444: lastDot + 1, bindingName.length);
445: if (matchesName(this .pattern.name, bindingName)) {
446: int level = matchField(fieldBinding, false);
447: if (level != IMPOSSIBLE_MATCH)
448: return level;
449: }
450: }
451: int otherMax = qNameRef.otherBindings == null ? 0
452: : qNameRef.otherBindings.length;
453: for (int i = 0; i < otherMax; i++) {
454: char[] token = qNameRef.tokens[i
455: + qNameRef.indexOfFirstFieldBinding];
456: if (matchesName(this .pattern.name, token)) {
457: FieldBinding otherBinding = qNameRef.otherBindings[i];
458: int level = matchField(otherBinding, false);
459: if (level != IMPOSSIBLE_MATCH)
460: return level;
461: }
462: }
463: return IMPOSSIBLE_MATCH;
464: }
465:
466: /* (non-Javadoc)
467: * Resolve level for type with a given binding.
468: */
469: protected int resolveLevelForType(TypeBinding typeBinding) {
470: FieldPattern fieldPattern = (FieldPattern) this .pattern;
471: TypeBinding fieldTypeBinding = typeBinding;
472: if (fieldTypeBinding != null
473: && fieldTypeBinding.isParameterizedType()) {
474: fieldTypeBinding = typeBinding.erasure();
475: }
476: return resolveLevelForType(fieldPattern.typeSimpleName,
477: fieldPattern.typeQualification, fieldPattern
478: .getTypeArguments(), 0, fieldTypeBinding);
479: }
480: }
|