KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > corext > refactoring > code > SourceAnalyzer


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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  * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fixes for:
11  * o bug "Inline refactoring showed bogus error" (see bugzilla
12  * https://bugs.eclipse.org/bugs/show_bug.cgi?id=42753)
13  * o Allow 'this' constructor to be inlined
14  * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38093)
15  *******************************************************************************/

16 package org.eclipse.jdt.internal.corext.refactoring.code;
17
18 import java.util.ArrayList JavaDoc;
19 import java.util.Collection JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.Map JavaDoc;
24
25 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
26
27 import org.eclipse.jdt.core.ITypeRoot;
28 import org.eclipse.jdt.core.JavaModelException;
29 import org.eclipse.jdt.core.compiler.IProblem;
30 import org.eclipse.jdt.core.dom.ASTNode;
31 import org.eclipse.jdt.core.dom.ASTVisitor;
32 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
33 import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
34 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
35 import org.eclipse.jdt.core.dom.ArrayAccess;
36 import org.eclipse.jdt.core.dom.Block;
37 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
38 import org.eclipse.jdt.core.dom.EnumDeclaration;
39 import org.eclipse.jdt.core.dom.Expression;
40 import org.eclipse.jdt.core.dom.FieldAccess;
41 import org.eclipse.jdt.core.dom.IBinding;
42 import org.eclipse.jdt.core.dom.IMethodBinding;
43 import org.eclipse.jdt.core.dom.ITypeBinding;
44 import org.eclipse.jdt.core.dom.IVariableBinding;
45 import org.eclipse.jdt.core.dom.MethodDeclaration;
46 import org.eclipse.jdt.core.dom.MethodInvocation;
47 import org.eclipse.jdt.core.dom.Modifier;
48 import org.eclipse.jdt.core.dom.Name;
49 import org.eclipse.jdt.core.dom.ReturnStatement;
50 import org.eclipse.jdt.core.dom.SimpleName;
51 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
52 import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
53 import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
54 import org.eclipse.jdt.core.dom.SuperMethodInvocation;
55 import org.eclipse.jdt.core.dom.ThisExpression;
56 import org.eclipse.jdt.core.dom.TypeDeclaration;
57 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
58
59 import org.eclipse.jdt.internal.corext.codemanipulation.ImportReferencesCollector;
60 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
61 import org.eclipse.jdt.internal.corext.dom.LocalVariableIndex;
62 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
63 import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
64 import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowContext;
65 import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowInfo;
66 import org.eclipse.jdt.internal.corext.refactoring.code.flow.InOutFlowAnalyzer;
67
68 class SourceAnalyzer {
69     
70     public static class NameData {
71         private String JavaDoc fName;
72         private List JavaDoc fReferences;
73         public NameData(String JavaDoc n) {
74             fName= n;
75             fReferences= new ArrayList JavaDoc(2);
76         }
77         public String JavaDoc getName() {
78             return fName;
79         }
80         public void addReference(SimpleName ref) {
81             fReferences.add(ref);
82         }
83         public List JavaDoc references() {
84             return fReferences;
85         }
86     }
87
88     private class ActivationAnalyzer extends ASTVisitor {
89         public RefactoringStatus status= new RefactoringStatus();
90         private ASTNode fLastNode= getLastNode();
91         private IMethodBinding fBinding= getBinding();
92         public boolean visit(ReturnStatement node) {
93             if (node != fLastNode) {
94                 fInterruptedExecutionFlow= true;
95             }
96             return true;
97         }
98         public boolean visit(EnumDeclaration node) {
99             return false;
100         }
101         public boolean visit(AnnotationTypeDeclaration node) {
102             return false;
103         }
104         public boolean visit(TypeDeclaration node) {
105             return false;
106         }
107         public boolean visit(AnonymousClassDeclaration node) {
108             return false;
109         }
110         public boolean visit(MethodInvocation node) {
111             IMethodBinding methodBinding= node.resolveMethodBinding();
112             if (methodBinding != null)
113                 methodBinding.getMethodDeclaration();
114             if (fBinding != null && methodBinding != null && fBinding.isEqualTo(methodBinding) && !status.hasFatalError()) {
115                 status.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_recursive_call);
116                 return false;
117             }
118             return true;
119         }
120         public boolean visit(SimpleName node) {
121             IBinding binding= node.resolveBinding();
122             if (binding == null && !status.hasFatalError()) {
123                 // fixes bug #42753
124
if (!ASTNodes.isLabel(node)) {
125                     status.addFatalError(
126                         RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_declaration_has_errors,
127                         JavaStatusContext.create(fTypeRoot, fDeclaration));
128                     return false;
129                 }
130             }
131             return true;
132         }
133         public boolean visit(ThisExpression node) {
134             if (node.getQualifier() != null) {
135                 status.addFatalError(
136                     RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_qualified_this_expressions,
137                     JavaStatusContext.create(fTypeRoot, node));
138                 return false;
139             }
140             return true;
141         }
142         private ASTNode getLastNode() {
143             List JavaDoc statements= fDeclaration.getBody().statements();
144             if (statements.size() == 0)
145                 return null;
146             return (ASTNode)statements.get(statements.size() - 1);
147         }
148         private IMethodBinding getBinding() {
149             IMethodBinding result= fDeclaration.resolveBinding();
150             if (result != null)
151                 return result.getMethodDeclaration();
152             return result;
153         }
154     }
155     
156     private class UpdateCollector extends ASTVisitor {
157         private int fTypeCounter;
158         public boolean visit(TypeDeclaration node) {
159             return visitType(node);
160         }
161         public void endVisit(TypeDeclaration node) {
162             fTypeCounter--;
163         }
164         public boolean visit(EnumDeclaration node) {
165             return visitType(node);
166         }
167         public void endVisit(EnumDeclaration node) {
168             fTypeCounter--;
169         }
170         public boolean visit(AnnotationTypeDeclaration node) {
171             return visitType(node);
172         }
173         public void endVisit(AnnotationTypeDeclaration node) {
174             fTypeCounter--;
175         }
176         private boolean visitType(AbstractTypeDeclaration node) {
177             if (fTypeCounter++ == 0) {
178                 addNameReference(node.getName());
179             }
180             return true;
181         }
182         public boolean visit(AnonymousClassDeclaration node) {
183             fTypeCounter++;
184             return true;
185         }
186         public void endVisit(AnonymousClassDeclaration node) {
187             fTypeCounter--;
188         }
189         public boolean visit(FieldAccess node) {
190             // only visit the expression and not the simple name
191
node.getExpression().accept(this);
192             addReferencesToName(node.getName());
193             return false;
194         }
195         public boolean visit(MethodDeclaration node) {
196             if (node.isConstructor()) {
197                 AbstractTypeDeclaration decl= (AbstractTypeDeclaration) ASTNodes.getParent(node, AbstractTypeDeclaration.class);
198                 NameData name= (NameData)fNames.get(decl.getName().resolveBinding());
199                 if (name != null) {
200                     name.addReference(node.getName());
201                 }
202             }
203             return true;
204         }
205         public boolean visit(MethodInvocation node) {
206             if (fTypeCounter == 0) {
207                 Expression receiver= node.getExpression();
208                 if (receiver == null && !isStaticallyImported(node.getName())) {
209                     fImplicitReceivers.add(node);
210                 }
211             }
212             return true;
213         }
214         public boolean visit(SuperMethodInvocation node) {
215             if (fTypeCounter == 0) {
216                 fHasSuperMethodInvocation= true;
217             }
218             return true;
219         }
220         public boolean visist(SuperConstructorInvocation node) {
221             if (fTypeCounter == 0) {
222                 fHasSuperMethodInvocation= true;
223             }
224             return true;
225         }
226         public boolean visit(ClassInstanceCreation node) {
227             if (fTypeCounter == 0) {
228                 Expression receiver= node.getExpression();
229                 if (receiver == null) {
230                     if (node.resolveTypeBinding().isLocal())
231                         fImplicitReceivers.add(node);
232                 }
233             }
234             return true;
235         }
236         public boolean visit(SingleVariableDeclaration node) {
237             if (fTypeCounter == 0)
238                 addNameReference(node.getName());
239             return true;
240         }
241         public boolean visit(VariableDeclarationFragment node) {
242             if (fTypeCounter == 0)
243                 addNameReference(node.getName());
244             return true;
245         }
246         public boolean visit(SimpleName node) {
247             addReferencesToName(node);
248             IBinding binding= node.resolveBinding();
249             if (binding instanceof ITypeBinding) {
250                 ITypeBinding type= (ITypeBinding)binding;
251                 if (type.isTypeVariable()) {
252                     addTypeVariableReference(type, node);
253                 }
254             } else if (binding instanceof IVariableBinding) {
255                 IVariableBinding vb= (IVariableBinding)binding;
256                 if (vb.isField() && ! isStaticallyImported(node)) {
257                     Name topName= ASTNodes.getTopMostName(node);
258                     if (node == topName || node == ASTNodes.getLeftMostSimpleName(topName)) {
259                         StructuralPropertyDescriptor location= node.getLocationInParent();
260                         if (location != SingleVariableDeclaration.NAME_PROPERTY
261                             && location != VariableDeclarationFragment.NAME_PROPERTY) {
262                             fImplicitReceivers.add(node);
263                         }
264                     }
265                 } else if (!vb.isField()) {
266                     // we have a local. Check if it is a parameter.
267
ParameterData data= (ParameterData)fParameters.get(binding);
268                     if (data != null) {
269                         ASTNode parent= node.getParent();
270                         if (parent instanceof Expression) {
271                             int precedence= OperatorPrecedence.getValue((Expression)parent);
272                             if (precedence != -1) {
273                                 data.setOperatorPrecedence(node, precedence);
274                             }
275                         }
276                     }
277                 }
278             }
279             return true;
280         }
281         public boolean visit(ThisExpression node) {
282             if (fTypeCounter == 0) {
283                 fImplicitReceivers.add(node);
284             }
285             return true;
286         }
287         private void addReferencesToName(SimpleName node) {
288             IBinding binding= node.resolveBinding();
289             ParameterData data= (ParameterData)fParameters.get(binding);
290             if (data != null)
291                 data.addReference(node);
292                 
293             NameData name= (NameData)fNames.get(binding);
294             if (name != null)
295                 name.addReference(node);
296         }
297         private void addNameReference(SimpleName name) {
298             fNames.put(name.resolveBinding(), new NameData(name.getIdentifier()));
299         }
300         private void addTypeVariableReference(ITypeBinding variable, SimpleName name) {
301             NameData data= (NameData)fTypeParameterMapping.get(variable);
302             if (data == null) {
303                 data= (NameData)fMethodTypeParameterMapping.get(variable);
304             }
305             data.addReference(name);
306         }
307         private boolean isStaticallyImported(Name name) {
308             return fStaticsToImport.contains(name);
309         }
310     }
311     
312     private class VarargAnalyzer extends ASTVisitor {
313         private IBinding fParameter;
314         public VarargAnalyzer(IBinding parameter) {
315             fParameter= parameter;
316         }
317         public boolean visit(ArrayAccess node) {
318             Expression array= node.getArray();
319             if (array instanceof SimpleName && fParameter.isEqualTo(((SimpleName)array).resolveBinding())) {
320                 fArrayAccess= true;
321             }
322             return true;
323         }
324     }
325
326     private ITypeRoot fTypeRoot;
327     private MethodDeclaration fDeclaration;
328     private Map JavaDoc fParameters;
329     private Map JavaDoc fNames;
330     private List JavaDoc fImplicitReceivers;
331     
332     private boolean fArrayAccess;
333     private boolean fHasSuperMethodInvocation;
334     
335     private List JavaDoc/*<Name>*/ fTypesToImport;
336     private List JavaDoc/*<Name>*/ fStaticsToImport;
337     
338     private List JavaDoc/*<NameData>*/ fTypeParameterReferences;
339     private Map JavaDoc/*<ITypeBinding, NameData>*/ fTypeParameterMapping;
340     
341     private List JavaDoc/*<NameData>*/ fMethodTypeParameterReferences;
342     private Map JavaDoc/*<ITypeBinding, NameData>*/ fMethodTypeParameterMapping;
343     
344     private boolean fInterruptedExecutionFlow;
345
346     public SourceAnalyzer(ITypeRoot typeRoot, MethodDeclaration declaration) {
347         super();
348         fTypeRoot= typeRoot;
349         fDeclaration= declaration;
350     }
351     
352     public boolean isExecutionFlowInterrupted() {
353         return fInterruptedExecutionFlow;
354     }
355     
356     public RefactoringStatus checkActivation() throws JavaModelException {
357         RefactoringStatus result= new RefactoringStatus();
358         if (!fTypeRoot.isStructureKnown()) {
359             result.addFatalError(
360                 RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_syntax_errors,
361                 JavaStatusContext.create(fTypeRoot));
362             return result;
363         }
364         IProblem[] problems= ASTNodes.getProblems(fDeclaration, ASTNodes.NODE_ONLY, ASTNodes.ERROR);
365         if (problems.length > 0) {
366             result.addFatalError(
367                 RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_declaration_has_errors,
368                 JavaStatusContext.create(fTypeRoot, fDeclaration));
369             return result;
370         }
371         final IMethodBinding declarationBinding= fDeclaration.resolveBinding();
372         if (declarationBinding != null) {
373             final int modifiers= declarationBinding.getModifiers();
374             if (Modifier.isAbstract(modifiers)) {
375                 result.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_abstract_methods, JavaStatusContext.create(fTypeRoot, fDeclaration));
376                 return result;
377             } else if (Modifier.isNative(modifiers)) {
378                 result.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_native_methods, JavaStatusContext.create(fTypeRoot, fDeclaration));
379                 return result;
380             }
381         } else {
382             result.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_methoddeclaration_has_errors, JavaStatusContext.create(fTypeRoot));
383             return result;
384         }
385         ActivationAnalyzer analyzer= new ActivationAnalyzer();
386         fDeclaration.accept(analyzer);
387         result.merge(analyzer.status);
388         if (!result.hasFatalError()) {
389             List JavaDoc parameters= fDeclaration.parameters();
390             fParameters= new HashMap JavaDoc(parameters.size() * 2);
391             for (Iterator JavaDoc iter= parameters.iterator(); iter.hasNext();) {
392                 SingleVariableDeclaration element= (SingleVariableDeclaration) iter.next();
393                 IVariableBinding binding= element.resolveBinding();
394                 if (binding == null) {
395                     result.addFatalError(
396                         RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_declaration_has_errors,
397                         JavaStatusContext.create(fTypeRoot, fDeclaration));
398                     return result;
399                 }
400                 fParameters.put(binding, element.getProperty(ParameterData.PROPERTY));
401             }
402             fNames= new HashMap JavaDoc();
403             fImplicitReceivers= new ArrayList JavaDoc(2);
404             
405             fTypeParameterReferences= new ArrayList JavaDoc(0);
406             fTypeParameterMapping= new HashMap JavaDoc();
407             ITypeBinding declaringType= declarationBinding.getDeclaringClass();
408             if (declaringType == null) {
409                 result.addFatalError(
410                     RefactoringCoreMessages.InlineMethodRefactoring_SourceAnalyzer_typedeclaration_has_errors,
411                     JavaStatusContext.create(fTypeRoot));
412                 return result;
413             }
414             ITypeBinding[] typeParameters= declaringType.getTypeParameters();
415             for (int i= 0; i < typeParameters.length; i++) {
416                 NameData data= new NameData(typeParameters[i].getName());
417                 fTypeParameterReferences.add(data);
418                 fTypeParameterMapping.put(typeParameters[i], data);
419             }
420             
421             fMethodTypeParameterReferences= new ArrayList JavaDoc(0);
422             fMethodTypeParameterMapping= new HashMap JavaDoc();
423             IMethodBinding method= declarationBinding;
424             typeParameters= method.getTypeParameters();
425             for (int i= 0; i < typeParameters.length; i++) {
426                 NameData data= new NameData(typeParameters[i].getName());
427                 fMethodTypeParameterReferences.add(data);
428                 fMethodTypeParameterMapping.put(typeParameters[i], data);
429             }
430             
431         }
432         if (fDeclaration.isVarargs()) {
433             List JavaDoc parameters= fDeclaration.parameters();
434             VarargAnalyzer vAnalyzer= new VarargAnalyzer(
435                 ((SingleVariableDeclaration)parameters.get(parameters.size() - 1)).getName().resolveBinding());
436             fDeclaration.getBody().accept(vAnalyzer);
437         }
438         return result;
439     }
440
441     public void initialize() {
442         Block body= fDeclaration.getBody();
443         // first collect the static imports. This is necessary to not mark
444
// static imported fields and methods as implicit visible.
445
fTypesToImport= new ArrayList JavaDoc();
446         fStaticsToImport= new ArrayList JavaDoc();
447         ImportReferencesCollector collector= new ImportReferencesCollector(
448             fTypeRoot.getJavaProject(), null, fTypesToImport, fStaticsToImport);
449         body.accept(collector);
450         
451         // Now collect implicit references and name references
452
body.accept(new UpdateCollector());
453         
454         int numberOfLocals= LocalVariableIndex.perform(fDeclaration);
455         FlowContext context= new FlowContext(0, numberOfLocals + 1);
456         context.setConsiderAccessMode(true);
457         context.setComputeMode(FlowContext.MERGE);
458         InOutFlowAnalyzer flowAnalyzer= new InOutFlowAnalyzer(context);
459         FlowInfo info= flowAnalyzer.perform(getStatements());
460         
461         for (Iterator JavaDoc iter= fDeclaration.parameters().iterator(); iter.hasNext();) {
462             SingleVariableDeclaration element= (SingleVariableDeclaration) iter.next();
463             IVariableBinding binding= element.resolveBinding();
464             ParameterData data= (ParameterData)element.getProperty(ParameterData.PROPERTY);
465             data.setAccessMode(info.getAccessMode(context, binding));
466         }
467     }
468     
469     public Collection JavaDoc getUsedNames() {
470         return fNames.values();
471     }
472     
473     public List JavaDoc getImplicitReceivers() {
474         return fImplicitReceivers;
475     }
476     
477     public List JavaDoc getTypesToImport() {
478         return fTypesToImport;
479     }
480     
481     public List JavaDoc getStaticsToImport() {
482         return fStaticsToImport;
483     }
484     
485     public List JavaDoc getTypeParameterReferences() {
486         return fTypeParameterReferences;
487     }
488     
489     public List JavaDoc getMethodTypeParameterReferences() {
490         return fMethodTypeParameterReferences;
491     }
492     
493     public boolean hasArrayAccess() {
494         return fArrayAccess;
495     }
496     
497     public boolean hasSuperMethodInvocation() {
498         return fHasSuperMethodInvocation;
499     }
500     
501     private ASTNode[] getStatements() {
502         List JavaDoc statements= fDeclaration.getBody().statements();
503         return (ASTNode[]) statements.toArray(new ASTNode[statements.size()]);
504     }
505
506 }
507
Popular Tags