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.corext.dom;
011:
012: import java.util.ArrayList;
013: import java.util.Collection;
014: import java.util.HashSet;
015: import java.util.List;
016:
017: import org.eclipse.jdt.core.dom.AST;
018: import org.eclipse.jdt.core.dom.ASTNode;
019: import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
020: import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
021: import org.eclipse.jdt.core.dom.Block;
022: import org.eclipse.jdt.core.dom.BodyDeclaration;
023: import org.eclipse.jdt.core.dom.CatchClause;
024: import org.eclipse.jdt.core.dom.ClassInstanceCreation;
025: import org.eclipse.jdt.core.dom.CompilationUnit;
026: import org.eclipse.jdt.core.dom.Expression;
027: import org.eclipse.jdt.core.dom.FieldAccess;
028: import org.eclipse.jdt.core.dom.ForStatement;
029: import org.eclipse.jdt.core.dom.IBinding;
030: import org.eclipse.jdt.core.dom.IMethodBinding;
031: import org.eclipse.jdt.core.dom.ITypeBinding;
032: import org.eclipse.jdt.core.dom.IVariableBinding;
033: import org.eclipse.jdt.core.dom.ImportDeclaration;
034: import org.eclipse.jdt.core.dom.Initializer;
035: import org.eclipse.jdt.core.dom.MethodDeclaration;
036: import org.eclipse.jdt.core.dom.MethodInvocation;
037: import org.eclipse.jdt.core.dom.Modifier;
038: import org.eclipse.jdt.core.dom.QualifiedName;
039: import org.eclipse.jdt.core.dom.SimpleName;
040: import org.eclipse.jdt.core.dom.Statement;
041: import org.eclipse.jdt.core.dom.SuperMethodInvocation;
042: import org.eclipse.jdt.core.dom.SwitchCase;
043: import org.eclipse.jdt.core.dom.SwitchStatement;
044: import org.eclipse.jdt.core.dom.Type;
045: import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
046: import org.eclipse.jdt.core.dom.TypeParameter;
047: import org.eclipse.jdt.core.dom.VariableDeclaration;
048: import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
049: import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
050:
051: import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
052:
053: /**
054: * Evaluates all fields, methods and types available (declared) at a given offset
055: * in a compilation unit (Code assist that returns IBindings)
056: */
057: public class ScopeAnalyzer {
058:
059: private static final IBinding[] NO_BINDING = new IBinding[0];
060:
061: /**
062: * Flag to specify that method should be reported.
063: */
064: public static final int METHODS = 1;
065:
066: /**
067: * Flag to specify that variables should be reported.
068: */
069: public static final int VARIABLES = 2;
070:
071: /**
072: * Flag to specify that types should be reported.
073: */
074: public static final int TYPES = 4;
075:
076: /**
077: * Flag to specify that only visible elements should be added.
078: */
079: public static final int CHECK_VISIBILITY = 16;
080:
081: private static interface IBindingRequestor {
082: boolean acceptBinding(IBinding binding);
083: }
084:
085: private static class DefaultBindingRequestor implements
086: IBindingRequestor {
087:
088: private final List fResult;
089: private final HashSet fNamesAdded;
090: private final int fFlags;
091: private final ITypeBinding fParentTypeBinding;
092:
093: public DefaultBindingRequestor(ITypeBinding parentTypeBinding,
094: int flags) {
095: fParentTypeBinding = parentTypeBinding;
096: fFlags = flags;
097: fResult = new ArrayList();
098: fNamesAdded = new HashSet();
099: }
100:
101: public DefaultBindingRequestor() {
102: this (null, 0);
103: }
104:
105: /**
106: * {@inheritDoc}
107: */
108: public boolean acceptBinding(IBinding binding) {
109: if (binding == null)
110: return false;
111:
112: String signature = getSignature(binding);
113: if (signature != null && fNamesAdded.add(signature)) { // avoid duplicated results from inheritance
114: fResult.add(binding);
115: }
116: return false;
117: }
118:
119: public List getResult() {
120: if (hasFlag(CHECK_VISIBILITY, fFlags)) {
121: for (int i = fResult.size() - 1; i >= 0; i--) {
122: IBinding binding = (IBinding) fResult.get(i);
123: if (!isVisible(binding, fParentTypeBinding)) {
124: fResult.remove(i);
125: }
126: }
127: }
128: return fResult;
129: }
130:
131: }
132:
133: private HashSet fTypesVisited;
134:
135: private CompilationUnit fRoot;
136:
137: public ScopeAnalyzer(CompilationUnit root) {
138: fTypesVisited = new HashSet();
139: fRoot = root;
140: }
141:
142: private void clearLists() {
143: fTypesVisited.clear();
144: }
145:
146: private static String getSignature(IBinding binding) {
147: if (binding != null) {
148: switch (binding.getKind()) {
149: case IBinding.METHOD:
150: StringBuffer buf = new StringBuffer();
151: buf.append('M');
152: buf.append(binding.getName()).append('(');
153: ITypeBinding[] parameters = ((IMethodBinding) binding)
154: .getParameterTypes();
155: for (int i = 0; i < parameters.length; i++) {
156: if (i > 0) {
157: buf.append(',');
158: }
159: ITypeBinding paramType = parameters[i].getErasure();
160: buf.append(paramType.getQualifiedName());
161: }
162: buf.append(')');
163: return buf.toString();
164: case IBinding.VARIABLE:
165: return 'V' + binding.getName();
166: case IBinding.TYPE:
167: return 'T' + binding.getName();
168: }
169: }
170: return null;
171: }
172:
173: static final boolean hasFlag(int property, int flags) {
174: return (flags & property) != 0;
175: }
176:
177: /**
178: * Collects all elements available in a type and its hierarchy
179: * @param binding The type binding
180: * @param flags Flags defining the elements to report
181: * @param requestor the requestor to which all results are reported
182: * @return return <code>true</code> if the requestor has reported the binding as found and no further results are required
183: */
184: private boolean addInherited(ITypeBinding binding, int flags,
185: IBindingRequestor requestor) {
186: if (!fTypesVisited.add(binding)) {
187: return false;
188: }
189: if (hasFlag(VARIABLES, flags)) {
190: IVariableBinding[] variableBindings = binding
191: .getDeclaredFields();
192: for (int i = 0; i < variableBindings.length; i++) {
193: if (requestor.acceptBinding(variableBindings[i]))
194: return true;
195: }
196: }
197:
198: if (hasFlag(METHODS, flags)) {
199: IMethodBinding[] methodBindings = binding
200: .getDeclaredMethods();
201: for (int i = 0; i < methodBindings.length; i++) {
202: IMethodBinding curr = methodBindings[i];
203: if (!curr.isSynthetic() && !curr.isConstructor()) {
204: if (requestor.acceptBinding(curr))
205: return true;
206: }
207: }
208: }
209:
210: if (hasFlag(TYPES, flags)) {
211: ITypeBinding[] typeBindings = binding.getDeclaredTypes();
212: for (int i = 0; i < typeBindings.length; i++) {
213: ITypeBinding curr = typeBindings[i];
214: if (requestor.acceptBinding(curr))
215: return true;
216: }
217: }
218:
219: ITypeBinding super Class = binding.getSuperclass();
220: if (super Class != null) {
221: if (addInherited(super Class, flags, requestor)) // recursive
222: return true;
223: } else if (binding.isArray()) {
224: if (addInherited(fRoot.getAST().resolveWellKnownType(
225: "java.lang.Object"), flags, requestor)) //$NON-NLS-1$
226: return true;
227: }
228:
229: ITypeBinding[] interfaces = binding.getInterfaces(); // includes looking for methods: abstract, unimplemented methods
230: for (int i = 0; i < interfaces.length; i++) {
231: if (addInherited(interfaces[i], flags, requestor)) // recursive
232: return true;
233: }
234: return false;
235: }
236:
237: /**
238: * Collects all elements available in a type: its hierarchy and its outer scopes.
239: * @param binding The type binding
240: * @param flags Flags defining the elements to report
241: * @param requestor the requestor to which all results are reported
242: * @return return <code>true</code> if the requestor has reported the binding as found and no further results are required
243: */
244: private boolean addTypeDeclarations(ITypeBinding binding,
245: int flags, IBindingRequestor requestor) {
246: if (hasFlag(TYPES, flags) && !binding.isAnonymous()) {
247: if (requestor.acceptBinding(binding))
248: return true;
249:
250: ITypeBinding[] typeParameters = binding.getTypeParameters();
251: for (int i = 0; i < typeParameters.length; i++) {
252: if (requestor.acceptBinding(typeParameters[i]))
253: return true;
254: }
255: }
256:
257: addInherited(binding, flags, requestor); // add inherited
258:
259: if (binding.isLocal()) {
260: addOuterDeclarationsForLocalType(binding, flags, requestor);
261: } else {
262: ITypeBinding declaringClass = binding.getDeclaringClass();
263: if (declaringClass != null) {
264: if (addTypeDeclarations(declaringClass, flags,
265: requestor)) // Recursively add inherited
266: return true;
267: } else if (hasFlag(TYPES, flags)) {
268: if (fRoot.findDeclaringNode(binding) != null) {
269: List types = fRoot.types();
270: for (int i = 0; i < types.size(); i++) {
271: if (requestor
272: .acceptBinding(((AbstractTypeDeclaration) types
273: .get(i)).resolveBinding()))
274: return true;
275: }
276: }
277: }
278: }
279: return false;
280: }
281:
282: private boolean addOuterDeclarationsForLocalType(
283: ITypeBinding localBinding, int flags,
284: IBindingRequestor requestor) {
285: ASTNode node = fRoot.findDeclaringNode(localBinding);
286: if (node == null) {
287: return false;
288: }
289:
290: if (node instanceof AbstractTypeDeclaration
291: || node instanceof AnonymousClassDeclaration) {
292: if (addLocalDeclarations(node.getParent(), flags, requestor))
293: return true;
294:
295: ITypeBinding parentTypeBinding = Bindings
296: .getBindingOfParentType(node.getParent());
297: if (parentTypeBinding != null) {
298: if (addTypeDeclarations(parentTypeBinding, flags,
299: requestor))
300: return true;
301: }
302:
303: }
304: return false;
305: }
306:
307: private static ITypeBinding getBinding(Expression node) {
308: if (node != null) {
309: return node.resolveTypeBinding();
310: }
311: return null;
312: }
313:
314: private static ITypeBinding getQualifier(SimpleName selector) {
315: ASTNode parent = selector.getParent();
316: switch (parent.getNodeType()) {
317: case ASTNode.METHOD_INVOCATION:
318: MethodInvocation decl = (MethodInvocation) parent;
319: if (selector == decl.getName()) {
320: return getBinding(decl.getExpression());
321: }
322: return null;
323: case ASTNode.QUALIFIED_NAME:
324: QualifiedName qualifiedName = (QualifiedName) parent;
325: if (selector == qualifiedName.getName()) {
326: return getBinding(qualifiedName.getQualifier());
327: }
328: return null;
329: case ASTNode.FIELD_ACCESS:
330: FieldAccess fieldAccess = (FieldAccess) parent;
331: if (selector == fieldAccess.getName()) {
332: return getBinding(fieldAccess.getExpression());
333: }
334: return null;
335: case ASTNode.SUPER_FIELD_ACCESS: {
336: ITypeBinding curr = Bindings.getBindingOfParentType(parent);
337: return curr.getSuperclass();
338: }
339: case ASTNode.SUPER_METHOD_INVOCATION: {
340: SuperMethodInvocation super Inv = (SuperMethodInvocation) parent;
341: if (selector == super Inv.getName()) {
342: ITypeBinding curr = Bindings
343: .getBindingOfParentType(parent);
344: return curr.getSuperclass();
345: }
346: return null;
347: }
348: default:
349: if (parent instanceof Type) {
350: // bug 67644: in 'a.new X()', all member types of A are visible as location of X.
351: ASTNode normalizedNode = ASTNodes
352: .getNormalizedNode(parent);
353: if (normalizedNode.getLocationInParent() == ClassInstanceCreation.TYPE_PROPERTY) {
354: ClassInstanceCreation creation = (ClassInstanceCreation) normalizedNode
355: .getParent();
356: return getBinding(creation.getExpression());
357: }
358: }
359: return null;
360: }
361: }
362:
363: public IBinding[] getDeclarationsInScope(SimpleName selector,
364: int flags) {
365: try {
366: // special case for switch on enum
367: if (selector.getLocationInParent() == SwitchCase.EXPRESSION_PROPERTY) {
368: ITypeBinding binding = ((SwitchStatement) selector
369: .getParent().getParent()).getExpression()
370: .resolveTypeBinding();
371: if (binding != null && binding.isEnum()) {
372: return getEnumContants(binding);
373: }
374: }
375:
376: ITypeBinding parentTypeBinding = Bindings
377: .getBindingOfParentType(selector);
378: if (parentTypeBinding != null) {
379: ITypeBinding binding = getQualifier(selector);
380: DefaultBindingRequestor requestor = new DefaultBindingRequestor(
381: parentTypeBinding, flags);
382: if (binding == null) {
383: addLocalDeclarations(selector, flags, requestor);
384: addTypeDeclarations(parentTypeBinding, flags,
385: requestor);
386: } else {
387: addInherited(binding, flags, requestor);
388: }
389:
390: List result = requestor.getResult();
391: return (IBinding[]) result.toArray(new IBinding[result
392: .size()]);
393: }
394: return NO_BINDING;
395: } finally {
396: clearLists();
397: }
398: }
399:
400: private static class SearchRequestor implements IBindingRequestor {
401:
402: private final int fFlags;
403: private final ITypeBinding fParentTypeBinding;
404: private final IBinding fToSearch;
405: private boolean fFound;
406: private boolean fIsVisible;
407:
408: public SearchRequestor(IBinding toSearch,
409: ITypeBinding parentTypeBinding, int flag) {
410: fFlags = flag;
411: fToSearch = toSearch;
412: fParentTypeBinding = parentTypeBinding;
413: fFound = false;
414: fIsVisible = true;
415: }
416:
417: public boolean acceptBinding(IBinding binding) {
418: if (fFound)
419: return true;
420:
421: if (binding == null)
422: return false;
423:
424: if (fToSearch.getKind() != binding.getKind()) {
425: return false;
426: }
427:
428: boolean checkVisibility = hasFlag(CHECK_VISIBILITY, fFlags);
429: if (binding == fToSearch) {
430: fFound = true;
431: } else {
432: IBinding bindingDeclaration = Bindings
433: .getDeclaration(binding);
434: if (bindingDeclaration == fToSearch) {
435: fFound = true;
436: } else if (bindingDeclaration.getName().equals(
437: fToSearch.getName())) {
438: String signature = getSignature(bindingDeclaration);
439: if (signature != null
440: && signature
441: .equals(getSignature(fToSearch))) {
442: if (checkVisibility) {
443: fIsVisible = false;
444: }
445: return true; // found element that hides the binding to find
446: }
447: }
448: }
449:
450: if (fFound && checkVisibility) {
451: fIsVisible = ScopeAnalyzer.isVisible(binding,
452: fParentTypeBinding);
453: }
454: return fFound;
455: }
456:
457: public boolean found() {
458: return fFound;
459: }
460:
461: public boolean isVisible() {
462: return fIsVisible;
463: }
464: }
465:
466: public boolean isDeclaredInScope(IBinding declaration,
467: SimpleName selector, int flags) {
468: try {
469: // special case for switch on enum
470: if (selector.getLocationInParent() == SwitchCase.EXPRESSION_PROPERTY) {
471: ITypeBinding binding = ((SwitchStatement) selector
472: .getParent().getParent()).getExpression()
473: .resolveTypeBinding();
474: if (binding != null && binding.isEnum()) {
475: return hasEnumContants(declaration, binding
476: .getTypeDeclaration());
477: }
478: }
479:
480: ITypeBinding parentTypeBinding = Bindings
481: .getBindingOfParentTypeContext(selector);
482: if (parentTypeBinding != null) {
483: ITypeBinding binding = getQualifier(selector);
484: SearchRequestor requestor = new SearchRequestor(
485: declaration, parentTypeBinding, flags);
486: if (binding == null) {
487: addLocalDeclarations(selector, flags, requestor);
488: if (requestor.found())
489: return requestor.isVisible();
490: addTypeDeclarations(parentTypeBinding, flags,
491: requestor);
492: if (requestor.found())
493: return requestor.isVisible();
494: } else {
495: addInherited(binding, flags, requestor);
496: if (requestor.found())
497: return requestor.isVisible();
498: }
499: }
500: return false;
501: } finally {
502: clearLists();
503: }
504: }
505:
506: private IVariableBinding[] getEnumContants(ITypeBinding binding) {
507: IVariableBinding[] declaredFields = binding.getDeclaredFields();
508: ArrayList res = new ArrayList(declaredFields.length);
509: for (int i = 0; i < declaredFields.length; i++) {
510: IVariableBinding curr = declaredFields[i];
511: if (curr.isEnumConstant()) {
512: res.add(curr);
513: }
514: }
515: return (IVariableBinding[]) res
516: .toArray(new IVariableBinding[res.size()]);
517: }
518:
519: private boolean hasEnumContants(IBinding declaration,
520: ITypeBinding binding) {
521: IVariableBinding[] declaredFields = binding.getDeclaredFields();
522: for (int i = 0; i < declaredFields.length; i++) {
523: IVariableBinding curr = declaredFields[i];
524: if (curr == declaration)
525: return true;
526: }
527: return false;
528: }
529:
530: public IBinding[] getDeclarationsInScope(int offset, int flags) {
531: NodeFinder finder = new NodeFinder(offset, 0);
532: fRoot.accept(finder);
533: ASTNode node = finder.getCoveringNode();
534: if (node == null) {
535: return NO_BINDING;
536: }
537:
538: if (node instanceof SimpleName) {
539: return getDeclarationsInScope((SimpleName) node, flags);
540: }
541:
542: try {
543: ITypeBinding binding = Bindings
544: .getBindingOfParentType(node);
545: DefaultBindingRequestor requestor = new DefaultBindingRequestor(
546: binding, flags);
547: addLocalDeclarations(node, offset, flags, requestor);
548: if (binding != null) {
549: addTypeDeclarations(binding, flags, requestor);
550: }
551: List result = requestor.getResult();
552: return (IBinding[]) result.toArray(new IBinding[result
553: .size()]);
554: } finally {
555: clearLists();
556: }
557: }
558:
559: private static ITypeBinding getDeclaringType(IBinding binding) {
560: switch (binding.getKind()) {
561: case IBinding.VARIABLE:
562: return ((IVariableBinding) binding).getDeclaringClass();
563: case IBinding.METHOD:
564: return ((IMethodBinding) binding).getDeclaringClass();
565: case IBinding.TYPE:
566: ITypeBinding typeBinding = (ITypeBinding) binding;
567: if (typeBinding.getDeclaringClass() != null) {
568: return typeBinding;
569: }
570: return typeBinding;
571: }
572: return null;
573: }
574:
575: /**
576: * Evaluates if the declaration is visible in a certain context.
577: * @param binding The binding of the declaration to examine
578: * @param context The context to test in
579: * @return Returns
580: */
581: public static boolean isVisible(IBinding binding,
582: ITypeBinding context) {
583: if (binding.getKind() == IBinding.VARIABLE
584: && !((IVariableBinding) binding).isField()) {
585: return true; // all local variables found are visible
586: }
587: ITypeBinding declaring = getDeclaringType(binding);
588: if (declaring == null) {
589: return false;
590: }
591:
592: int modifiers = binding.getModifiers();
593: if (Modifier.isPublic(modifiers) || declaring.isInterface()) {
594: return true;
595: } else if (Modifier.isProtected(modifiers)
596: || !Modifier.isPrivate(modifiers)) {
597: if (declaring.getPackage() == context.getPackage()) {
598: return true;
599: }
600: return isTypeInScope(declaring, context, Modifier
601: .isProtected(modifiers));
602: }
603: // private visibility
604: return isTypeInScope(declaring, context, false);
605: }
606:
607: private static boolean isTypeInScope(ITypeBinding declaring,
608: ITypeBinding context, boolean includeHierarchy) {
609: ITypeBinding curr = context;
610: while (curr != null && curr != declaring) {
611: if (includeHierarchy
612: && Bindings.isSuperType(declaring, curr)) {
613: return true;
614: }
615: curr = curr.getDeclaringClass();
616: }
617: return curr == declaring;
618: }
619:
620: public IBinding[] getDeclarationsAfter(int offset, int flags) {
621: try {
622: NodeFinder finder = new NodeFinder(offset, 0);
623: fRoot.accept(finder);
624: ASTNode node = finder.getCoveringNode();
625: if (node == null) {
626: return null;
627: }
628:
629: ASTNode declaration = ASTResolving
630: .findParentStatement(node);
631: while (declaration instanceof Statement
632: && declaration.getNodeType() != ASTNode.BLOCK) {
633: declaration = declaration.getParent();
634: }
635:
636: if (declaration instanceof Block) {
637: DefaultBindingRequestor requestor = new DefaultBindingRequestor();
638: DeclarationsAfterVisitor visitor = new DeclarationsAfterVisitor(
639: node.getStartPosition(), flags, requestor);
640: declaration.accept(visitor);
641: List result = requestor.getResult();
642: return (IBinding[]) result.toArray(new IBinding[result
643: .size()]);
644: }
645: return NO_BINDING;
646: } finally {
647: clearLists();
648: }
649: }
650:
651: private class ScopeAnalyzerVisitor extends HierarchicalASTVisitor {
652:
653: private int fPosition;
654: private int fFlags;
655: private final IBindingRequestor fRequestor;
656: private boolean fBreak;
657:
658: public ScopeAnalyzerVisitor(int position, int flags,
659: IBindingRequestor requestor) {
660: fPosition = position;
661: fFlags = flags;
662: fRequestor = requestor;
663: fBreak = false;
664: }
665:
666: private boolean isInside(ASTNode node) {
667: int start = node.getStartPosition();
668: int end = start + node.getLength();
669:
670: return start <= fPosition && fPosition < end;
671: }
672:
673: public boolean visit(MethodDeclaration node) {
674: if (isInside(node)) {
675: Block body = node.getBody();
676: if (body != null) {
677: body.accept(this );
678: }
679: visitBackwards(node.parameters());
680: if (node.getAST().apiLevel() >= AST.JLS3) {
681: visitBackwards(node.typeParameters());
682: }
683: }
684: return false;
685: }
686:
687: /* (non-Javadoc)
688: * @see org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor#visit(org.eclipse.jdt.core.dom.TypeParameter)
689: */
690: public boolean visit(TypeParameter node) {
691: if (hasFlag(TYPES, fFlags)
692: && node.getStartPosition() < fPosition) {
693: fBreak = fRequestor.acceptBinding(node.getName()
694: .resolveBinding());
695: }
696: return !fBreak;
697: }
698:
699: public boolean visit(SwitchCase node) {
700: // switch on enum allows to use enum constants without qualification
701: if (hasFlag(VARIABLES, fFlags) && !node.isDefault()
702: && isInside(node.getExpression())) {
703: SwitchStatement switchStatement = (SwitchStatement) node
704: .getParent();
705: ITypeBinding binding = switchStatement.getExpression()
706: .resolveTypeBinding();
707: if (binding != null && binding.isEnum()) {
708: IVariableBinding[] declaredFields = binding
709: .getDeclaredFields();
710: for (int i = 0; i < declaredFields.length; i++) {
711: IVariableBinding curr = declaredFields[i];
712: if (curr.isEnumConstant()) {
713: fBreak = fRequestor.acceptBinding(curr);
714: if (fBreak)
715: return false;
716: }
717: }
718: }
719: }
720: return false;
721: }
722:
723: public boolean visit(Initializer node) {
724: return !fBreak && isInside(node);
725: }
726:
727: public boolean visit(Statement node) {
728: return !fBreak && isInside(node);
729: }
730:
731: public boolean visit(ASTNode node) {
732: return false;
733: }
734:
735: public boolean visit(Block node) {
736: if (isInside(node)) {
737: visitBackwards(node.statements());
738: }
739: return false;
740: }
741:
742: public boolean visit(VariableDeclaration node) {
743: if (hasFlag(VARIABLES, fFlags)
744: && node.getStartPosition() < fPosition) {
745: fBreak = fRequestor
746: .acceptBinding(node.resolveBinding());
747: }
748: return !fBreak;
749: }
750:
751: public boolean visit(VariableDeclarationStatement node) {
752: visitBackwards(node.fragments());
753: return false;
754: }
755:
756: public boolean visit(VariableDeclarationExpression node) {
757: visitBackwards(node.fragments());
758: return false;
759: }
760:
761: public boolean visit(CatchClause node) {
762: if (isInside(node)) {
763: node.getBody().accept(this );
764: node.getException().accept(this );
765: }
766: return false;
767: }
768:
769: public boolean visit(ForStatement node) {
770: if (isInside(node)) {
771: node.getBody().accept(this );
772: visitBackwards(node.initializers());
773: }
774: return false;
775: }
776:
777: public boolean visit(TypeDeclarationStatement node) {
778: if (hasFlag(TYPES, fFlags)
779: && node.getStartPosition() + node.getLength() < fPosition) {
780: fBreak = fRequestor
781: .acceptBinding(node.resolveBinding());
782: return false;
783: }
784: return !fBreak && isInside(node);
785: }
786:
787: private void visitBackwards(List list) {
788: if (fBreak)
789: return;
790:
791: for (int i = list.size() - 1; i >= 0; i--) {
792: ASTNode curr = (ASTNode) list.get(i);
793: if (curr.getStartPosition() < fPosition) {
794: curr.accept(this );
795: }
796: }
797: }
798: }
799:
800: private class DeclarationsAfterVisitor extends
801: HierarchicalASTVisitor {
802: private final int fPosition;
803: private final int fFlags;
804: private final IBindingRequestor fRequestor;
805: private boolean fBreak;
806:
807: public DeclarationsAfterVisitor(int position, int flags,
808: IBindingRequestor requestor) {
809: fPosition = position;
810: fFlags = flags;
811: fRequestor = requestor;
812: fBreak = false;
813: }
814:
815: public boolean visit(ASTNode node) {
816: return !fBreak;
817: }
818:
819: public boolean visit(VariableDeclaration node) {
820: if (hasFlag(VARIABLES, fFlags)
821: && fPosition < node.getStartPosition()) {
822: fBreak = fRequestor
823: .acceptBinding(node.resolveBinding());
824: }
825: return false;
826: }
827:
828: public boolean visit(AnonymousClassDeclaration node) {
829: return false;
830: }
831:
832: public boolean visit(TypeDeclarationStatement node) {
833: if (hasFlag(TYPES, fFlags)
834: && fPosition < node.getStartPosition()) {
835: fBreak = fRequestor
836: .acceptBinding(node.resolveBinding());
837: }
838: return false;
839: }
840: }
841:
842: private boolean addLocalDeclarations(ASTNode node, int flags,
843: IBindingRequestor requestor) {
844: return addLocalDeclarations(node, node.getStartPosition(),
845: flags, requestor);
846: }
847:
848: private boolean addLocalDeclarations(ASTNode node, int offset,
849: int flags, IBindingRequestor requestor) {
850: if (hasFlag(VARIABLES, flags) || hasFlag(TYPES, flags)) {
851: BodyDeclaration declaration = ASTResolving
852: .findParentBodyDeclaration(node);
853: if (declaration instanceof MethodDeclaration
854: || declaration instanceof Initializer) {
855: ScopeAnalyzerVisitor visitor = new ScopeAnalyzerVisitor(
856: offset, flags, requestor);
857: declaration.accept(visitor);
858: return visitor.fBreak;
859: }
860: }
861: return false;
862: }
863:
864: public Collection getUsedVariableNames(int offset, int length) {
865: HashSet result = new HashSet();
866: IBinding[] bindingsBefore = getDeclarationsInScope(offset,
867: VARIABLES);
868: for (int i = 0; i < bindingsBefore.length; i++) {
869: result.add(bindingsBefore[i].getName());
870: }
871: IBinding[] bindingsAfter = getDeclarationsAfter(
872: offset + length, VARIABLES);
873: for (int i = 0; i < bindingsAfter.length; i++) {
874: result.add(bindingsAfter[i].getName());
875: }
876: List imports = fRoot.imports();
877: for (int i = 0; i < imports.size(); i++) {
878: ImportDeclaration decl = (ImportDeclaration) imports.get(i);
879: if (decl.isStatic() && !decl.isOnDemand()) {
880: result.add(ASTNodes.getSimpleNameIdentifier(decl
881: .getName()));
882: }
883: }
884: return result;
885: }
886: }
|