]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-after/ui/org/eclipse/jdt/internal/ui/search/MethodExitsFinder.java
Merge branch 'master' of git.uio.no:ifi-stolz-refaktor
[ifi-stolz-refaktor.git] / case-study / jdt-after / ui / org / eclipse / jdt / internal / ui / search / MethodExitsFinder.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2011 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.ui.search;
12
13 import java.util.ArrayList;
14 import java.util.Iterator;
15 import java.util.List;
16
17 import org.eclipse.jdt.core.dom.AST;
18 import org.eclipse.jdt.core.dom.ASTNode;
19 import org.eclipse.jdt.core.dom.ASTVisitor;
20 import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
21 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
22 import org.eclipse.jdt.core.dom.Block;
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.ConstructorInvocation;
27 import org.eclipse.jdt.core.dom.EnumDeclaration;
28 import org.eclipse.jdt.core.dom.IMethodBinding;
29 import org.eclipse.jdt.core.dom.ITypeBinding;
30 import org.eclipse.jdt.core.dom.MethodDeclaration;
31 import org.eclipse.jdt.core.dom.MethodInvocation;
32 import org.eclipse.jdt.core.dom.Name;
33 import org.eclipse.jdt.core.dom.NodeFinder;
34 import org.eclipse.jdt.core.dom.ReturnStatement;
35 import org.eclipse.jdt.core.dom.SimpleName;
36 import org.eclipse.jdt.core.dom.Statement;
37 import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
38 import org.eclipse.jdt.core.dom.SuperMethodInvocation;
39 import org.eclipse.jdt.core.dom.ThrowStatement;
40 import org.eclipse.jdt.core.dom.TryStatement;
41 import org.eclipse.jdt.core.dom.Type;
42 import org.eclipse.jdt.core.dom.TypeDeclaration;
43 import org.eclipse.jdt.core.dom.UnionType;
44 import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
45 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
46
47 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
48 import org.eclipse.jdt.internal.corext.dom.Bindings;
49 import org.eclipse.jdt.internal.corext.dom.LocalVariableIndex;
50 import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowContext;
51 import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowInfo;
52 import org.eclipse.jdt.internal.corext.refactoring.code.flow.InOutFlowAnalyzer;
53 import org.eclipse.jdt.internal.corext.util.Messages;
54
55 import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
56
57
58 public class MethodExitsFinder extends ASTVisitor implements IOccurrencesFinder {
59
60         public static final String ID= "MethodExitsFinder"; //$NON-NLS-1$
61
62         private MethodDeclaration fMethodDeclaration;
63         private List<OccurrenceLocation> fResult;
64         private List<ITypeBinding> fCaughtExceptions;
65         private String fExitDescription;
66         private CompilationUnit fASTRoot;
67
68         public String initialize(CompilationUnit root, int offset, int length) {
69                 return initialize(root, NodeFinder.perform(root, offset, length));
70         }
71
72         /**
73          * @param root the AST root
74          * @param node the selected node
75          * @return returns a message if there is a problem
76          */
77         public String initialize(CompilationUnit root, ASTNode node) {
78                 fASTRoot= root;
79
80                 if (node instanceof ReturnStatement) {
81                         fMethodDeclaration= (MethodDeclaration)ASTNodes.getParent(node, ASTNode.METHOD_DECLARATION);
82                         if (fMethodDeclaration == null)
83                                 return SearchMessages.MethodExitsFinder_no_return_type_selected;
84                         return null;
85
86                 }
87
88                 Type type= null;
89                 if (node instanceof Type) {
90                         type= (Type)node;
91                 } else if (node instanceof Name) {
92                         Name name= ASTNodes.getTopMostName((Name)node);
93                         if (name.getParent() instanceof Type) {
94                                 type= (Type)name.getParent();
95                         }
96                 }
97                 if (type == null)
98                         return SearchMessages.MethodExitsFinder_no_return_type_selected;
99                 type= ASTNodes.getTopMostType(type);
100                 if (!(type.getParent() instanceof MethodDeclaration))
101                         return SearchMessages.MethodExitsFinder_no_return_type_selected;
102                 fMethodDeclaration= (MethodDeclaration)type.getParent();
103
104                 fExitDescription= Messages.format(SearchMessages.MethodExitsFinder_occurrence_exit_description, BasicElementLabels.getJavaElementName(fMethodDeclaration.getName().toString()));
105                 return null;
106         }
107
108         private void performSearch() {
109                 fResult= new ArrayList<OccurrenceLocation>();
110                 markReferences();
111                 if (!fResult.isEmpty()) {
112                         Type returnType= fMethodDeclaration.getReturnType2();
113                         if (returnType != null) {
114                                 String desc= Messages.format(SearchMessages.MethodExitsFinder_occurrence_return_description, BasicElementLabels.getJavaElementName(fMethodDeclaration.getName().toString()));
115                                 fResult.add(new OccurrenceLocation(returnType.getStartPosition(), returnType.getLength(), 0, desc));
116                         }
117                 }
118         }
119
120         public OccurrenceLocation[] getOccurrences() {
121                 performSearch();
122                 if (fResult.isEmpty())
123                         return null;
124
125                 return fResult.toArray(new OccurrenceLocation[fResult.size()]);
126         }
127
128
129         private void markReferences() {
130                 fCaughtExceptions= new ArrayList<ITypeBinding>();
131                 boolean isVoid= true;
132                 Type returnType= fMethodDeclaration.getReturnType2();
133                 if (returnType != null) {
134                         ITypeBinding returnTypeBinding= returnType.resolveBinding();
135                         isVoid= returnTypeBinding != null && Bindings.isVoidType(returnTypeBinding);
136                 }
137                 fMethodDeclaration.accept(this);
138                 Block block= fMethodDeclaration.getBody();
139                 if (block != null) {
140                         List<Statement> statements= block.statements();
141                         if (statements.size() > 0) {
142                                 Statement last= statements.get(statements.size() - 1);
143                                 int maxVariableId= LocalVariableIndex.perform(fMethodDeclaration);
144                                 FlowContext flowContext= new FlowContext(0, maxVariableId + 1);
145                                 flowContext.generated_4776247409396246388();
146                                 InOutFlowAnalyzer flowAnalyzer= new InOutFlowAnalyzer(flowContext);
147                                 FlowInfo info= flowAnalyzer.perform(new ASTNode[] {last});
148                                 if (!info.isNoReturn() && !isVoid) {
149                                         if (!info.isPartialReturn())
150                                                 return;
151                                 }
152                         }
153                         int offset= fMethodDeclaration.getStartPosition() + fMethodDeclaration.getLength() - 1; // closing bracket
154                         fResult.add(new OccurrenceLocation(offset, 1, 0, fExitDescription));
155                 }
156         }
157
158         @Override
159         public boolean visit(TypeDeclaration node) {
160                 // Don't dive into a local type.
161                 return false;
162         }
163
164         @Override
165         public boolean visit(AnonymousClassDeclaration node) {
166                 // Don't dive into a local type.
167                 return false;
168         }
169
170         @Override
171         public boolean visit(AnnotationTypeDeclaration node) {
172                 // Don't dive into a local type.
173                 return false;
174         }
175
176         @Override
177         public boolean visit(EnumDeclaration node) {
178                 // Don't dive into a local type.
179                 return false;
180         }
181
182         @Override
183         public boolean visit(ReturnStatement node) {
184                 fResult.add(new OccurrenceLocation(node.getStartPosition(), node.getLength(), 0, fExitDescription));
185                 return super.visit(node);
186         }
187
188         @Override
189         public boolean visit(TryStatement node) {
190                 int currentSize= fCaughtExceptions.size();
191                 List<CatchClause> catchClauses= node.catchClauses();
192                 for (Iterator<CatchClause> iter= catchClauses.iterator(); iter.hasNext();) {
193                         Type type= iter.next().getException().getType();
194                         if (type instanceof UnionType) {
195                                 List<Type> types= ((UnionType) type).types();
196                                 for (Iterator<Type> iterator= types.iterator(); iterator.hasNext();) {
197                                         addCaughtException(iterator.next());
198                                 }
199                         } else {
200                                 addCaughtException(type);
201                         }
202                 }
203                 node.getBody().accept(this);
204
205                 if (node.getAST().apiLevel() >= AST.JLS4) {
206                         List<VariableDeclarationExpression> resources= node.resources();
207                         for (Iterator<VariableDeclarationExpression> iterator= resources.iterator(); iterator.hasNext();) {
208                                 iterator.next().accept(this);
209                         }
210
211                         //check if the method could exit as a result of resource#close()
212                         boolean exitMarked= false;
213                         for (VariableDeclarationExpression variable : resources) {
214                                 Type type= variable.getType();
215                                 IMethodBinding methodBinding= Bindings.findMethodInHierarchy(type.resolveBinding(), "close", new ITypeBinding[0]); //$NON-NLS-1$
216                                 if (methodBinding != null) {
217                                         ITypeBinding[] exceptionTypes= methodBinding.getExceptionTypes();
218                                         for (int j= 0; j < exceptionTypes.length; j++) {
219                                                 if (isExitPoint(exceptionTypes[j])) { // a close() throws an uncaught exception
220                                                         // mark name of resource
221                                                         for (VariableDeclarationFragment fragment : (List<VariableDeclarationFragment>) variable.fragments()) {
222                                                                 SimpleName name= fragment.getName();
223                                                                 fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fExitDescription));
224                                                         }
225                                                         if (!exitMarked) {
226                                                                 // mark exit position
227                                                                 exitMarked= true;
228                                                                 Block body= node.getBody();
229                                                                 int offset= body.getStartPosition() + body.getLength() - 1; // closing bracket of try block
230                                                                 fResult.add(new OccurrenceLocation(offset, 1, 0, Messages.format(SearchMessages.MethodExitsFinder_occurrence_exit_impclict_close_description,
231                                                                                 BasicElementLabels.getJavaElementName(fMethodDeclaration.getName().toString()))));
232                                                         }
233                                                 }
234                                         }
235                                 }
236                         }
237                 }
238
239                 int toRemove= fCaughtExceptions.size() - currentSize;
240                 for (int i= toRemove; i > 0; i--) {
241                         fCaughtExceptions.remove(currentSize);
242                 }
243
244                 // visit catch and finally
245                 for (Iterator<CatchClause> iter= catchClauses.iterator(); iter.hasNext();) {
246                         iter.next().accept(this);
247                 }
248                 if (node.getFinally() != null)
249                         node.getFinally().accept(this);
250
251                 // return false. We have visited the body by ourselves.
252                 return false;
253         }
254
255         private void addCaughtException(Type type) {
256                 ITypeBinding typeBinding= type.resolveBinding();
257                 if (typeBinding != null) {
258                         fCaughtExceptions.add(typeBinding);
259                 }
260         }
261
262         @Override
263         public boolean visit(ThrowStatement node) {
264                 ITypeBinding exception= node.getExpression().resolveTypeBinding();
265                 if (isExitPoint(exception)) {
266                         // mark 'throw'
267                         fResult.add(new OccurrenceLocation(node.getStartPosition(), 5, 0, fExitDescription));
268                 }
269                 return true;
270         }
271
272         @Override
273         public boolean visit(MethodInvocation node) {
274                 if (isExitPoint(node.resolveMethodBinding())) {
275                         SimpleName name= node.getName();
276                         fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fExitDescription));
277                 }
278                 return true;
279         }
280
281         @Override
282         public boolean visit(SuperMethodInvocation node) {
283                 if (isExitPoint(node.resolveMethodBinding())) {
284                         SimpleName name= node.getName();
285                         fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fExitDescription));
286                 }
287                 return true;
288         }
289
290         @Override
291         public boolean visit(ClassInstanceCreation node) {
292                 if (isExitPoint(node.resolveConstructorBinding())) {
293                         Type name= node.getType();
294                         fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fExitDescription));
295                 }
296                 return true;
297         }
298
299         @Override
300         public boolean visit(ConstructorInvocation node) {
301                 if (isExitPoint(node.resolveConstructorBinding())) {
302                         // mark 'this'
303                         fResult.add(new OccurrenceLocation(node.getStartPosition(), 4, 0, fExitDescription));
304                 }
305                 return true;
306         }
307
308         @Override
309         public boolean visit(SuperConstructorInvocation node) {
310                 if (isExitPoint(node.resolveConstructorBinding())) {
311                         // mark 'super'
312                         fResult.add(new OccurrenceLocation(node.getStartPosition(), 5, 0, fExitDescription));
313                 }
314                 return true;
315         }
316
317         private boolean isExitPoint(ITypeBinding binding) {
318                 if (binding == null)
319                         return false;
320                 return !isCaught(binding);
321         }
322
323         private boolean isExitPoint(IMethodBinding binding) {
324                 if (binding == null)
325                         return false;
326                 ITypeBinding[] exceptions= binding.getExceptionTypes();
327                 for (int i= 0; i < exceptions.length; i++) {
328                         if (!isCaught(exceptions[i]))
329                                 return true;
330                 }
331                 return false;
332         }
333
334         private boolean isCaught(ITypeBinding binding) {
335                 for (Iterator<ITypeBinding> iter= fCaughtExceptions.iterator(); iter.hasNext();) {
336                         ITypeBinding catchException= iter.next();
337                         if (catches(catchException, binding))
338                                 return true;
339                 }
340                 return false;
341         }
342
343         private boolean catches(ITypeBinding catchTypeBinding, ITypeBinding throwTypeBinding) {
344                 while(throwTypeBinding != null) {
345                         if (throwTypeBinding == catchTypeBinding)
346                                 return true;
347                         throwTypeBinding= throwTypeBinding.getSuperclass();
348                 }
349                 return false;
350         }
351
352         public CompilationUnit getASTRoot() {
353                 return fASTRoot;
354         }
355
356         public String getElementName() {
357                 return fMethodDeclaration.getName().getIdentifier();
358         }
359
360         public String getID() {
361                 return ID;
362         }
363
364         public String getJobLabel() {
365                 return SearchMessages.MethodExitsFinder_job_label;
366         }
367
368         public int getSearchKind() {
369                 return IOccurrencesFinder.K_BREAK_TARGET_OCCURRENCE;
370         }
371
372         public String getUnformattedPluralLabel() {
373                 return SearchMessages.MethodExitsFinder_label_plural;
374         }
375
376         public String getUnformattedSingularLabel() {
377                 return SearchMessages.MethodExitsFinder_label_singular;
378         }
379
380 }