]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-before/core extension/org/eclipse/jdt/internal/corext/dom/ScopeAnalyzer.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-before / core extension / org / eclipse / jdt / internal / corext / dom / ScopeAnalyzer.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2012 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.corext.dom;
12
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.HashSet;
16 import java.util.List;
17
18 import org.eclipse.jdt.core.dom.ASTNode;
19 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
20 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
21 import org.eclipse.jdt.core.dom.Block;
22 import org.eclipse.jdt.core.dom.BodyDeclaration;
23 import org.eclipse.jdt.core.dom.CatchClause;
24 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
25 import org.eclipse.jdt.core.dom.CompilationUnit;
26 import org.eclipse.jdt.core.dom.Expression;
27 import org.eclipse.jdt.core.dom.FieldAccess;
28 import org.eclipse.jdt.core.dom.ForStatement;
29 import org.eclipse.jdt.core.dom.IBinding;
30 import org.eclipse.jdt.core.dom.IMethodBinding;
31 import org.eclipse.jdt.core.dom.ITypeBinding;
32 import org.eclipse.jdt.core.dom.IVariableBinding;
33 import org.eclipse.jdt.core.dom.ImportDeclaration;
34 import org.eclipse.jdt.core.dom.Initializer;
35 import org.eclipse.jdt.core.dom.MethodDeclaration;
36 import org.eclipse.jdt.core.dom.MethodInvocation;
37 import org.eclipse.jdt.core.dom.Modifier;
38 import org.eclipse.jdt.core.dom.QualifiedName;
39 import org.eclipse.jdt.core.dom.SimpleName;
40 import org.eclipse.jdt.core.dom.Statement;
41 import org.eclipse.jdt.core.dom.SuperMethodInvocation;
42 import org.eclipse.jdt.core.dom.SwitchCase;
43 import org.eclipse.jdt.core.dom.SwitchStatement;
44 import org.eclipse.jdt.core.dom.Type;
45 import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
46 import org.eclipse.jdt.core.dom.TypeParameter;
47 import org.eclipse.jdt.core.dom.VariableDeclaration;
48 import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
49 import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
50
51 import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
52
53 /**
54  * Evaluates all fields, methods and types available (declared) at a given offset
55  * in a compilation unit (Code assist that returns IBindings)
56  */
57 public class ScopeAnalyzer {
58
59         private static final IBinding[] NO_BINDING= new IBinding[0];
60
61         /**
62          * Flag to specify that method should be reported.
63          */
64         public static final int METHODS= 1;
65
66         /**
67          * Flag to specify that variables should be reported.
68          */
69         public static final int VARIABLES= 2;
70
71         /**
72          * Flag to specify that types should be reported.
73          */
74         public static final int TYPES= 4;
75
76         /**
77          * Flag to specify that only visible elements should be added.
78          */
79         public static final int CHECK_VISIBILITY= 16;
80
81         private static interface IBindingRequestor {
82                 boolean acceptBinding(IBinding binding);
83         }
84
85         private static class DefaultBindingRequestor implements IBindingRequestor {
86
87                 private final List<IBinding> fResult;
88                 private final HashSet<String> fNamesAdded;
89                 private final int fFlags;
90                 private final ITypeBinding fParentTypeBinding;
91
92                 public DefaultBindingRequestor(ITypeBinding parentTypeBinding, int flags) {
93                         fParentTypeBinding= parentTypeBinding;
94                         fFlags= flags;
95                         fResult= new ArrayList<IBinding>();
96                         fNamesAdded= new HashSet<String>();
97                 }
98
99                 public DefaultBindingRequestor() {
100                         this(null, 0);
101                 }
102
103                 /**
104                  * {@inheritDoc}
105                  */
106                 public boolean acceptBinding(IBinding binding) {
107                         if (binding == null)
108                                 return false;
109
110                         String signature= getSignature(binding);
111                         if (signature != null && fNamesAdded.add(signature)) { // avoid duplicated results from inheritance
112                                 fResult.add(binding);
113                         }
114                         return false;
115                 }
116
117                 public List<IBinding> getResult() {
118                         if (hasFlag(CHECK_VISIBILITY, fFlags)) {
119                                 for (int i= fResult.size() - 1; i >= 0; i--) {
120                                         IBinding binding= fResult.get(i);
121                                         if (!isVisible(binding, fParentTypeBinding)) {
122                                                 fResult.remove(i);
123                                         }
124                                 }
125                         }
126                         return fResult;
127                 }
128
129         }
130
131         private final HashSet<ITypeBinding> fTypesVisited;
132
133         private final CompilationUnit fRoot;
134
135         public ScopeAnalyzer(CompilationUnit root) {
136                 fTypesVisited= new HashSet<ITypeBinding>();
137                 fRoot= root;
138         }
139
140         private void clearLists() {
141                 fTypesVisited.clear();
142         }
143
144         private static String getSignature(IBinding binding) {
145                 if (binding != null) {
146                         switch (binding.getKind()) {
147                                 case IBinding.METHOD:
148                                         StringBuffer buf= new StringBuffer();
149                                         buf.append('M');
150                                         buf.append(binding.getName()).append('(');
151                                         ITypeBinding[] parameters= ((IMethodBinding) binding).getParameterTypes();
152                                         for (int i= 0; i < parameters.length; i++) {
153                                                 if (i > 0) {
154                                                         buf.append(',');
155                                                 }
156                                                 ITypeBinding paramType= parameters[i].getErasure();
157                                                 buf.append(paramType.getQualifiedName());
158                                         }
159                                         buf.append(')');
160                                         return buf.toString();
161                                 case IBinding.VARIABLE:
162                                         return 'V' + binding.getName();
163                                 case IBinding.TYPE:
164                                         return 'T' + binding.getName();
165                         }
166                 }
167                 return null;
168         }
169
170         static final boolean hasFlag(int property, int flags) {
171                 return (flags & property) != 0;
172         }
173
174         /**
175          * Collects all elements available in a type and its hierarchy
176          * @param binding The type binding
177          * @param flags Flags defining the elements to report
178          * @param requestor the requestor to which all results are reported
179          * @return return <code>true</code> if the requestor has reported the binding as found and no further results are required
180          */
181         private boolean addInherited(ITypeBinding binding, int flags, IBindingRequestor requestor) {
182                 if (!fTypesVisited.add(binding)) {
183                         return false;
184                 }
185                 if (hasFlag(VARIABLES, flags)) {
186                         IVariableBinding[] variableBindings= binding.getDeclaredFields();
187                         for (int i= 0; i < variableBindings.length; i++) {
188                                 if (requestor.acceptBinding(variableBindings[i]))
189                                         return true;
190                         }
191                 }
192
193                 if (hasFlag(METHODS, flags)) {
194                         IMethodBinding[] methodBindings= binding.getDeclaredMethods();
195                         for (int i= 0; i < methodBindings.length; i++) {
196                                 IMethodBinding curr= methodBindings[i];
197                                 if (!curr.isSynthetic() && !curr.isConstructor()) {
198                                         if (requestor.acceptBinding(curr))
199                                                 return true;
200                                 }
201                         }
202                 }
203
204                 if (hasFlag(TYPES, flags)) {
205                         ITypeBinding[] typeBindings= binding.getDeclaredTypes();
206                         for (int i= 0; i < typeBindings.length; i++) {
207                                 ITypeBinding curr= typeBindings[i];
208                                 if (requestor.acceptBinding(curr))
209                                         return true;
210                         }
211                 }
212
213
214                 ITypeBinding superClass= binding.getSuperclass();
215                 if (superClass != null) {
216                         if (addInherited(superClass, flags, requestor)) // recursive
217                                 return true;
218                 } else if (binding.isArray()) {
219                         if (addInherited(fRoot.getAST().resolveWellKnownType("java.lang.Object"), flags, requestor)) //$NON-NLS-1$
220                                 return true;
221                 }
222
223                 ITypeBinding[] interfaces= binding.getInterfaces(); // includes looking for methods: abstract, unimplemented methods
224                 for (int i= 0; i < interfaces.length; i++) {
225                         if (addInherited(interfaces[i], flags, requestor)) // recursive
226                                 return true;
227                 }
228                 return false;
229         }
230
231
232         /**
233          * Collects all elements available in a type: its hierarchy and its outer scopes.
234          * @param binding The type binding
235          * @param flags Flags defining the elements to report
236          * @param requestor the requestor to which all results are reported
237          * @return return <code>true</code> if the requestor has reported the binding as found and no further results are required
238          */
239         private boolean addTypeDeclarations(ITypeBinding binding, int flags, IBindingRequestor requestor) {
240                 if (hasFlag(TYPES, flags) && !binding.isAnonymous()) {
241                         if (requestor.acceptBinding(binding))
242                                 return true;
243
244                         ITypeBinding[] typeParameters= binding.getTypeParameters();
245                         for (int i= 0; i < typeParameters.length; i++) {
246                                 if (requestor.acceptBinding(typeParameters[i]))
247                                         return true;
248                         }
249                 }
250
251                 addInherited(binding, flags, requestor); // add inherited
252
253                 if (binding.isLocal()) {
254                         addOuterDeclarationsForLocalType(binding, flags, requestor);
255                 } else {
256                         ITypeBinding declaringClass= binding.getDeclaringClass();
257                         if (declaringClass != null) {
258                                 if (addTypeDeclarations(declaringClass, flags, requestor)) // Recursively add inherited
259                                         return true;
260                         } else if (hasFlag(TYPES, flags)) {
261                                 if (fRoot.findDeclaringNode(binding) != null) {
262                                         List<AbstractTypeDeclaration> types= fRoot.types();
263                                         for (int i= 0; i < types.size(); i++) {
264                                                 if (requestor.acceptBinding(types.get(i).resolveBinding()))
265                                                         return true;
266                                         }
267                                 }
268                         }
269                 }
270                 return false;
271         }
272
273         private boolean addOuterDeclarationsForLocalType(ITypeBinding localBinding, int flags, IBindingRequestor requestor) {
274                 ASTNode node= fRoot.findDeclaringNode(localBinding);
275                 if (node == null) {
276                         return false;
277                 }
278
279                 if (node instanceof AbstractTypeDeclaration || node instanceof AnonymousClassDeclaration) {
280                         if (addLocalDeclarations(node.getParent(), flags, requestor))
281                                 return true;
282
283                         ITypeBinding parentTypeBinding= Bindings.getBindingOfParentType(node.getParent());
284                         if (parentTypeBinding != null) {
285                                 if (addTypeDeclarations(parentTypeBinding, flags, requestor))
286                                         return true;
287                         }
288
289                 }
290                 return false;
291         }
292
293         private static ITypeBinding getBinding(Expression node) {
294                 if (node != null) {
295                         return node.resolveTypeBinding();
296                 }
297                 return null;
298         }
299
300         private static ITypeBinding getQualifier(SimpleName selector) {
301                 ASTNode parent= selector.getParent();
302                 switch (parent.getNodeType()) {
303                         case ASTNode.METHOD_INVOCATION:
304                                 MethodInvocation decl= (MethodInvocation) parent;
305                                 if (selector == decl.getName()) {
306                                         return getBinding(decl.getExpression());
307                                 }
308                                 return null;
309                         case ASTNode.QUALIFIED_NAME:
310                                 QualifiedName qualifiedName= (QualifiedName) parent;
311                                 if (selector == qualifiedName.getName()) {
312                                         return getBinding(qualifiedName.getQualifier());
313                                 }
314                                 return null;
315                         case ASTNode.FIELD_ACCESS:
316                                 FieldAccess fieldAccess= (FieldAccess) parent;
317                                 if (selector == fieldAccess.getName()) {
318                                         return getBinding(fieldAccess.getExpression());
319                                 }
320                                 return null;
321                         case ASTNode.SUPER_FIELD_ACCESS: {
322                                 ITypeBinding curr= Bindings.getBindingOfParentType(parent);
323                                 return curr.getSuperclass();
324                         }
325                         case ASTNode.SUPER_METHOD_INVOCATION: {
326                                 SuperMethodInvocation superInv= (SuperMethodInvocation) parent;
327                                 if (selector == superInv.getName()) {
328                                         ITypeBinding curr= Bindings.getBindingOfParentType(parent);
329                                         return curr.getSuperclass();
330                                 }
331                                 return null;
332                         }
333                         default:
334                                 if (parent instanceof Type) {
335                                         // bug 67644: in 'a.new X()', all member types of A are visible as location of X.
336                                         ASTNode normalizedNode= ASTNodes.getNormalizedNode(parent);
337                                         if (normalizedNode.getLocationInParent() == ClassInstanceCreation.TYPE_PROPERTY) {
338                                                 ClassInstanceCreation creation= (ClassInstanceCreation) normalizedNode.getParent();
339                                                 return getBinding(creation.getExpression());
340                                         }
341                                 }
342                                 return null;
343                 }
344         }
345
346         public IBinding[] getDeclarationsInScope(SimpleName selector, int flags) {
347                 try {
348                         // special case for switch on enum
349                         if (selector.getLocationInParent() == SwitchCase.EXPRESSION_PROPERTY) {
350                                 ITypeBinding binding= ((SwitchStatement) selector.getParent().getParent()).getExpression().resolveTypeBinding();
351                                 if (binding != null && binding.isEnum()) {
352                                         return getEnumContants(binding);
353                                 }
354                         }
355
356                         ITypeBinding parentTypeBinding= Bindings.getBindingOfParentType(selector);
357                         if (parentTypeBinding != null) {
358                                 ITypeBinding binding= getQualifier(selector);
359                                 DefaultBindingRequestor requestor= new DefaultBindingRequestor(parentTypeBinding, flags);
360                                 if (binding == null) {
361                                         addLocalDeclarations(selector, flags, requestor);
362                                         addTypeDeclarations(parentTypeBinding, flags, requestor);
363                                 } else {
364                                         addInherited(binding, flags, requestor);
365                                 }
366
367                                 List<IBinding> result= requestor.getResult();
368                                 return result.toArray(new IBinding[result.size()]);
369                         }
370                         return NO_BINDING;
371                 } finally {
372                         clearLists();
373                 }
374         }
375
376         private static class SearchRequestor implements IBindingRequestor {
377
378                 private final int fFlags;
379                 private final ITypeBinding fParentTypeBinding;
380                 private final IBinding fToSearch;
381                 private boolean fFound;
382                 private boolean fIsVisible;
383
384                 public SearchRequestor(IBinding toSearch, ITypeBinding parentTypeBinding, int flag) {
385                         fFlags= flag;
386                         fToSearch= toSearch;
387                         fParentTypeBinding= parentTypeBinding;
388                         fFound= false;
389                         fIsVisible= true;
390                 }
391
392                 public boolean acceptBinding(IBinding binding) {
393                         if (fFound)
394                                 return true;
395
396                         if (binding == null)
397                                 return false;
398
399                         if (fToSearch.getKind() != binding.getKind()) {
400                                 return false;
401                         }
402
403                         boolean checkVisibility= hasFlag(CHECK_VISIBILITY, fFlags);
404                         if (binding == fToSearch) {
405                                 fFound= true;
406                         } else {
407                                 IBinding bindingDeclaration= Bindings.getDeclaration(binding);
408                                 if (bindingDeclaration == fToSearch) {
409                                         fFound= true;
410                                 } else if (bindingDeclaration.getName().equals(fToSearch.getName())) {
411                                         String signature= getSignature(bindingDeclaration);
412                                         if (signature != null && signature.equals(getSignature(fToSearch))) {
413                                                 if (checkVisibility) {
414                                                         fIsVisible= false;
415                                                 }
416                                                 return true; // found element that hides the binding to find
417                                         }
418                                 }
419                         }
420
421                         if (fFound && checkVisibility) {
422                                 fIsVisible= ScopeAnalyzer.isVisible(binding, fParentTypeBinding);
423                         }
424                         return fFound;
425                 }
426
427                 public boolean found() {
428                         return fFound;
429                 }
430
431                 public boolean isVisible() {
432                         return fIsVisible;
433                 }
434         }
435
436         public boolean isDeclaredInScope(IBinding declaration, SimpleName selector, int flags) {
437                 try {
438                         // special case for switch on enum
439                         if (selector.getLocationInParent() == SwitchCase.EXPRESSION_PROPERTY) {
440                                 ITypeBinding binding= ((SwitchStatement) selector.getParent().getParent()).getExpression().resolveTypeBinding();
441                                 if (binding != null && binding.isEnum()) {
442                                         return hasEnumContants(declaration, binding.getTypeDeclaration());
443                                 }
444                         }
445
446                         ITypeBinding parentTypeBinding= Bindings.getBindingOfParentTypeContext(selector);
447                         if (parentTypeBinding != null) {
448                                 ITypeBinding binding= getQualifier(selector);
449                                 SearchRequestor requestor= new SearchRequestor(declaration, parentTypeBinding, flags);
450                                 if (binding == null) {
451                                         addLocalDeclarations(selector, flags, requestor);
452                                         if (requestor.found())
453                                                 return requestor.isVisible();
454                                         addTypeDeclarations(parentTypeBinding, flags, requestor);
455                                         if (requestor.found())
456                                                 return requestor.isVisible();
457                                 } else {
458                                         addInherited(binding, flags, requestor);
459                                         if (requestor.found())
460                                                 return requestor.isVisible();
461                                 }
462                         }
463                         return false;
464                 } finally {
465                         clearLists();
466                 }
467         }
468
469         private IVariableBinding[] getEnumContants(ITypeBinding binding) {
470                 IVariableBinding[] declaredFields= binding.getDeclaredFields();
471                 ArrayList<IVariableBinding> res= new ArrayList<IVariableBinding>(declaredFields.length);
472                 for (int i= 0; i < declaredFields.length; i++) {
473                         IVariableBinding curr= declaredFields[i];
474                         if (curr.isEnumConstant()) {
475                                 res.add(curr);
476                         }
477                 }
478                 return res.toArray(new IVariableBinding[res.size()]);
479         }
480
481         private boolean hasEnumContants(IBinding declaration, ITypeBinding binding) {
482                 IVariableBinding[] declaredFields= binding.getDeclaredFields();
483                 for (int i= 0; i < declaredFields.length; i++) {
484                         IVariableBinding curr= declaredFields[i];
485                         if (curr == declaration)
486                                 return true;
487                 }
488                 return false;
489         }
490
491         public IBinding[] getDeclarationsInScope(int offset, int flags) {
492                 org.eclipse.jdt.core.dom.NodeFinder finder= new org.eclipse.jdt.core.dom.NodeFinder(fRoot, offset, 0);
493                 ASTNode node= finder.getCoveringNode();
494                 if (node == null) {
495                         return NO_BINDING;
496                 }
497
498                 if (node instanceof SimpleName) {
499                         return getDeclarationsInScope((SimpleName) node, flags);
500                 }
501
502                 try {
503                         ITypeBinding binding= Bindings.getBindingOfParentType(node);
504                         DefaultBindingRequestor requestor= new DefaultBindingRequestor(binding, flags);
505                         addLocalDeclarations(node, offset, flags, requestor);
506                         if (binding != null) {
507                                 addTypeDeclarations(binding, flags, requestor);
508                         }
509                         List<IBinding> result= requestor.getResult();
510                         return result.toArray(new IBinding[result.size()]);
511                 } finally {
512                         clearLists();
513                 }
514         }
515
516         private static ITypeBinding getDeclaringType(IBinding binding) {
517                 switch (binding.getKind()) {
518                         case IBinding.VARIABLE:
519                                 return ((IVariableBinding) binding).getDeclaringClass();
520                         case IBinding.METHOD:
521                                 return ((IMethodBinding) binding).getDeclaringClass();
522                         case IBinding.TYPE:
523                                 ITypeBinding typeBinding= (ITypeBinding) binding;
524                                 if (typeBinding.getDeclaringClass() != null) {
525                                         return typeBinding;
526                                 }
527                                 return typeBinding;
528                 }
529                 return null;
530         }
531
532         /**
533          * Evaluates if the declaration is visible in a certain context.
534          * @param binding The binding of the declaration to examine
535          * @param context The context to test in
536          * @return Returns
537          */
538         public static boolean isVisible(IBinding binding, ITypeBinding context) {
539                 if (binding.getKind() == IBinding.VARIABLE && !((IVariableBinding) binding).isField()) {
540                         return true; // all local variables found are visible
541                 }
542                 ITypeBinding declaring= getDeclaringType(binding);
543                 if (declaring == null) {
544                         return false;
545                 }
546
547                 declaring= declaring.getTypeDeclaration();
548
549                 int modifiers= binding.getModifiers();
550                 if (Modifier.isPublic(modifiers) || declaring.isInterface()) {
551                         return true;
552                 } else if (Modifier.isProtected(modifiers) || !Modifier.isPrivate(modifiers)) {
553                         if (declaring.getPackage() == context.getPackage()) {
554                                 return true;
555                         }
556                         return isTypeInScope(declaring, context, Modifier.isProtected(modifiers));
557                 }
558                 // private visibility
559                 return isTypeInScope(declaring, context, false);
560         }
561
562         private static boolean isTypeInScope(ITypeBinding declaring, ITypeBinding context, boolean includeHierarchy) {
563                 ITypeBinding curr= context.getTypeDeclaration();
564                 while (curr != null && curr != declaring) {
565                         if (includeHierarchy && isInSuperTypeHierarchy(declaring, curr)) {
566                                 return true;
567                         }
568                         curr= curr.getDeclaringClass();
569                 }
570                 return curr == declaring;
571         }
572
573         /*
574          * This method is different from Binding.isSuperType as type declarations are compared
575          */
576         private static boolean isInSuperTypeHierarchy(ITypeBinding possibleSuperTypeDecl, ITypeBinding type) {
577                 if (type == possibleSuperTypeDecl) {
578                         return true;
579                 }
580                 ITypeBinding superClass= type.getSuperclass();
581                 if (superClass != null) {
582                         if (isInSuperTypeHierarchy(possibleSuperTypeDecl, superClass.getTypeDeclaration())) {
583                                 return true;
584                         }
585                 }
586                 if (possibleSuperTypeDecl.isInterface()) {
587                         ITypeBinding[] superInterfaces= type.getInterfaces();
588                         for (int i= 0; i < superInterfaces.length; i++) {
589                                 if (isInSuperTypeHierarchy(possibleSuperTypeDecl, superInterfaces[i].getTypeDeclaration())) {
590                                         return true;
591                                 }
592                         }
593                 }
594                 return false;
595         }
596
597
598         public IBinding[] getDeclarationsAfter(int offset, int flags) {
599                 try {
600                         org.eclipse.jdt.core.dom.NodeFinder finder= new org.eclipse.jdt.core.dom.NodeFinder(fRoot, offset, 0);
601                         ASTNode node= finder.getCoveringNode();
602                         if (node == null) {
603                                 return null;
604                         }
605
606                         ASTNode declaration= ASTResolving.findParentStatement(node);
607                         while (declaration instanceof Statement && declaration.getNodeType() != ASTNode.BLOCK) {
608                                 declaration= declaration.getParent();
609                         }
610
611                         if (declaration instanceof Block) {
612                                 DefaultBindingRequestor requestor= new DefaultBindingRequestor();
613                                 DeclarationsAfterVisitor visitor= new DeclarationsAfterVisitor(node.getStartPosition(), flags, requestor);
614                                 declaration.accept(visitor);
615                                 List<IBinding> result= requestor.getResult();
616                                 return result.toArray(new IBinding[result.size()]);
617                         }
618                         return NO_BINDING;
619                 } finally {
620                         clearLists();
621                 }
622         }
623
624
625         private class ScopeAnalyzerVisitor extends HierarchicalASTVisitor {
626
627                 private final int fPosition;
628                 private final int fFlags;
629                 private final IBindingRequestor fRequestor;
630                 private boolean fBreak;
631
632                 public ScopeAnalyzerVisitor(int position, int flags, IBindingRequestor requestor) {
633                         fPosition= position;
634                         fFlags= flags;
635                         fRequestor= requestor;
636                         fBreak= false;
637                 }
638
639                 private boolean isInside(ASTNode node) {
640                         int start= node.getStartPosition();
641                         int end= start + node.getLength();
642
643                         return start <= fPosition && fPosition < end;
644                 }
645
646                 @Override
647                 public boolean visit(MethodDeclaration node) {
648                         if (isInside(node)) {
649                                 Block body= node.getBody();
650                                 if (body != null) {
651                                         body.accept(this);
652                                 }
653                                 visitBackwards(node.parameters());
654                                 visitBackwards(node.typeParameters());
655                         }
656                         return false;
657                 }
658
659
660                 /* (non-Javadoc)
661                  * @see org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor#visit(org.eclipse.jdt.core.dom.TypeParameter)
662                  */
663                 @Override
664                 public boolean visit(TypeParameter node) {
665                         if (hasFlag(TYPES, fFlags) && node.getStartPosition() < fPosition) {
666                                 fBreak= fRequestor.acceptBinding(node.getName().resolveBinding());
667                         }
668                         return !fBreak;
669                 }
670
671                 @Override
672                 public boolean visit(SwitchCase node) {
673                         // switch on enum allows to use enum constants without qualification
674                         if (hasFlag(VARIABLES, fFlags) && !node.isDefault() && isInside(node.getExpression())) {
675                                 SwitchStatement switchStatement= (SwitchStatement) node.getParent();
676                                 ITypeBinding binding= switchStatement.getExpression().resolveTypeBinding();
677                                 if (binding != null && binding.isEnum()) {
678                                         IVariableBinding[] declaredFields= binding.getDeclaredFields();
679                                         for (int i= 0; i < declaredFields.length; i++) {
680                                                 IVariableBinding curr= declaredFields[i];
681                                                 if (curr.isEnumConstant()) {
682                                                         fBreak= fRequestor.acceptBinding(curr);
683                                                         if (fBreak)
684                                                                 return false;
685                                                 }
686                                         }
687                                 }
688                         }
689                         return false;
690                 }
691
692                 @Override
693                 public boolean visit(Initializer node) {
694                         return !fBreak && isInside(node);
695                 }
696
697                 @Override
698                 public boolean visit(Statement node) {
699                         return !fBreak && isInside(node);
700                 }
701
702                 @Override
703                 public boolean visit(ASTNode node) {
704                         return false;
705                 }
706
707                 @Override
708                 public boolean visit(Block node) {
709                         if (isInside(node)) {
710                                 visitBackwards(node.statements());
711                         }
712                         return false;
713                 }
714
715                 @Override
716                 public boolean visit(VariableDeclaration node) {
717                         if (hasFlag(VARIABLES, fFlags) && node.getStartPosition() < fPosition) {
718                                 fBreak= fRequestor.acceptBinding(node.resolveBinding());
719                         }
720                         return !fBreak;
721                 }
722
723                 @Override
724                 public boolean visit(VariableDeclarationStatement node) {
725                         visitBackwards(node.fragments());
726                         return false;
727                 }
728
729                 @Override
730                 public boolean visit(VariableDeclarationExpression node) {
731                         visitBackwards(node.fragments());
732                         return false;
733                 }
734
735                 @Override
736                 public boolean visit(CatchClause node) {
737                         if (isInside(node)) {
738                                 node.getBody().accept(this);
739                                 node.getException().accept(this);
740                         }
741                         return false;
742                 }
743
744                 @Override
745                 public boolean visit(ForStatement node) {
746                         if (isInside(node)) {
747                                 node.getBody().accept(this);
748                                 visitBackwards(node.initializers());
749                         }
750                         return false;
751                 }
752
753                 @Override
754                 public boolean visit(TypeDeclarationStatement node) {
755                         if (hasFlag(TYPES, fFlags) && node.getStartPosition() + node.getLength() < fPosition) {
756                                 fBreak= fRequestor.acceptBinding(node.resolveBinding());
757                                 return false;
758                         }
759                         return !fBreak && isInside(node);
760                 }
761
762                 private void visitBackwards(List<? extends ASTNode> list) {
763                         if (fBreak)
764                                 return;
765
766                         for (int i= list.size() - 1; i >= 0; i--) {
767                                 ASTNode curr= list.get(i);
768                                 if (curr.getStartPosition() <  fPosition) {
769                                         curr.accept(this);
770                                 }
771                         }
772                 }
773         }
774
775         private class DeclarationsAfterVisitor extends HierarchicalASTVisitor {
776                 private final int fPosition;
777                 private final int fFlags;
778                 private final IBindingRequestor fRequestor;
779                 private boolean fBreak;
780
781                 public DeclarationsAfterVisitor(int position, int flags, IBindingRequestor requestor) {
782                         fPosition= position;
783                         fFlags= flags;
784                         fRequestor= requestor;
785                         fBreak= false;
786                 }
787
788                 @Override
789                 public boolean visit(ASTNode node) {
790                         return !fBreak;
791                 }
792
793                 @Override
794                 public boolean visit(VariableDeclaration node) {
795                         if (hasFlag(VARIABLES, fFlags) && fPosition < node.getStartPosition()) {
796                                 fBreak= fRequestor.acceptBinding(node.resolveBinding());
797                         }
798                         return false;
799                 }
800
801                 @Override
802                 public boolean visit(AnonymousClassDeclaration node) {
803                         return false;
804                 }
805
806                 @Override
807                 public boolean visit(TypeDeclarationStatement node) {
808                         if (hasFlag(TYPES, fFlags) && fPosition < node.getStartPosition()) {
809                                 fBreak= fRequestor.acceptBinding(node.resolveBinding());
810                         }
811                         return false;
812                 }
813         }
814
815         private boolean addLocalDeclarations(ASTNode node, int flags, IBindingRequestor requestor) {
816                 return addLocalDeclarations(node, node.getStartPosition(), flags, requestor);
817         }
818
819
820         private boolean addLocalDeclarations(ASTNode node, int offset, int flags, IBindingRequestor requestor) {
821                 if (hasFlag(VARIABLES, flags) || hasFlag(TYPES, flags)) {
822                         BodyDeclaration declaration= ASTResolving.findParentBodyDeclaration(node);
823                         if (declaration instanceof MethodDeclaration || declaration instanceof Initializer) {
824                                 ScopeAnalyzerVisitor visitor= new ScopeAnalyzerVisitor(offset, flags, requestor);
825                                 declaration.accept(visitor);
826                                 return visitor.fBreak;
827                         }
828                 }
829                 return false;
830         }
831
832         public Collection<String> getUsedVariableNames(int offset, int length) {
833                 HashSet<String> result= new HashSet<String>();
834                 IBinding[] bindingsBefore= getDeclarationsInScope(offset, VARIABLES);
835                 for (int i= 0; i < bindingsBefore.length; i++) {
836                         result.add(bindingsBefore[i].getName());
837                 }
838                 IBinding[] bindingsAfter= getDeclarationsAfter(offset + length, VARIABLES);
839                 for (int i= 0; i < bindingsAfter.length; i++) {
840                         result.add(bindingsAfter[i].getName());
841                 }
842                 List<ImportDeclaration> imports= fRoot.imports();
843                 for (int i= 0; i < imports.size(); i++) {
844                         ImportDeclaration decl= imports.get(i);
845                         if (decl.isStatic() && !decl.isOnDemand()) {
846                                 result.add(ASTNodes.getSimpleNameIdentifier(decl.getName()));
847                         }
848                 }
849                 return result;
850         }
851 }