]> git.uio.no Git - ifi-stolz-refaktor.git/blobdiff - case-study/jdt-before/ui/org/eclipse/jdt/internal/ui/search/MethodExitsFinder.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-before / ui / org / eclipse / jdt / internal / ui / search / MethodExitsFinder.java
diff --git a/case-study/jdt-before/ui/org/eclipse/jdt/internal/ui/search/MethodExitsFinder.java b/case-study/jdt-before/ui/org/eclipse/jdt/internal/ui/search/MethodExitsFinder.java
new file mode 100644 (file)
index 0000000..9208804
--- /dev/null
@@ -0,0 +1,381 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.ui.search;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.CatchClause;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ConstructorInvocation;
+import org.eclipse.jdt.core.dom.EnumDeclaration;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.NodeFinder;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.ThrowStatement;
+import org.eclipse.jdt.core.dom.TryStatement;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.UnionType;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.Bindings;
+import org.eclipse.jdt.internal.corext.dom.LocalVariableIndex;
+import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowContext;
+import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowInfo;
+import org.eclipse.jdt.internal.corext.refactoring.code.flow.InOutFlowAnalyzer;
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
+
+
+public class MethodExitsFinder extends ASTVisitor implements IOccurrencesFinder {
+
+       public static final String ID= "MethodExitsFinder"; //$NON-NLS-1$
+
+       private MethodDeclaration fMethodDeclaration;
+       private List<OccurrenceLocation> fResult;
+       private List<ITypeBinding> fCaughtExceptions;
+       private String fExitDescription;
+       private CompilationUnit fASTRoot;
+
+       public String initialize(CompilationUnit root, int offset, int length) {
+               return initialize(root, NodeFinder.perform(root, offset, length));
+       }
+
+       /**
+        * @param root the AST root
+        * @param node the selected node
+        * @return returns a message if there is a problem
+        */
+       public String initialize(CompilationUnit root, ASTNode node) {
+               fASTRoot= root;
+
+               if (node instanceof ReturnStatement) {
+                       fMethodDeclaration= (MethodDeclaration)ASTNodes.getParent(node, ASTNode.METHOD_DECLARATION);
+                       if (fMethodDeclaration == null)
+                               return SearchMessages.MethodExitsFinder_no_return_type_selected;
+                       return null;
+
+               }
+
+               Type type= null;
+               if (node instanceof Type) {
+                       type= (Type)node;
+               } else if (node instanceof Name) {
+                       Name name= ASTNodes.getTopMostName((Name)node);
+                       if (name.getParent() instanceof Type) {
+                               type= (Type)name.getParent();
+                       }
+               }
+               if (type == null)
+                       return SearchMessages.MethodExitsFinder_no_return_type_selected;
+               type= ASTNodes.getTopMostType(type);
+               if (!(type.getParent() instanceof MethodDeclaration))
+                       return SearchMessages.MethodExitsFinder_no_return_type_selected;
+               fMethodDeclaration= (MethodDeclaration)type.getParent();
+
+               fExitDescription= Messages.format(SearchMessages.MethodExitsFinder_occurrence_exit_description, BasicElementLabels.getJavaElementName(fMethodDeclaration.getName().toString()));
+               return null;
+       }
+
+       private void performSearch() {
+               fResult= new ArrayList<OccurrenceLocation>();
+               markReferences();
+               if (!fResult.isEmpty()) {
+                       Type returnType= fMethodDeclaration.getReturnType2();
+                       if (returnType != null) {
+                               String desc= Messages.format(SearchMessages.MethodExitsFinder_occurrence_return_description, BasicElementLabels.getJavaElementName(fMethodDeclaration.getName().toString()));
+                               fResult.add(new OccurrenceLocation(returnType.getStartPosition(), returnType.getLength(), 0, desc));
+                       }
+               }
+       }
+
+       public OccurrenceLocation[] getOccurrences() {
+               performSearch();
+               if (fResult.isEmpty())
+                       return null;
+
+               return fResult.toArray(new OccurrenceLocation[fResult.size()]);
+       }
+
+
+       private void markReferences() {
+               fCaughtExceptions= new ArrayList<ITypeBinding>();
+               boolean isVoid= true;
+               Type returnType= fMethodDeclaration.getReturnType2();
+               if (returnType != null) {
+                       ITypeBinding returnTypeBinding= returnType.resolveBinding();
+                       isVoid= returnTypeBinding != null && Bindings.isVoidType(returnTypeBinding);
+               }
+               fMethodDeclaration.accept(this);
+               Block block= fMethodDeclaration.getBody();
+               if (block != null) {
+                       List<Statement> statements= block.statements();
+                       if (statements.size() > 0) {
+                               Statement last= statements.get(statements.size() - 1);
+                               int maxVariableId= LocalVariableIndex.perform(fMethodDeclaration);
+                               FlowContext flowContext= new FlowContext(0, maxVariableId + 1);
+                               flowContext.setConsiderAccessMode(false);
+                               flowContext.setComputeMode(FlowContext.ARGUMENTS);
+                               InOutFlowAnalyzer flowAnalyzer= new InOutFlowAnalyzer(flowContext);
+                               FlowInfo info= flowAnalyzer.perform(new ASTNode[] {last});
+                               if (!info.isNoReturn() && !isVoid) {
+                                       if (!info.isPartialReturn())
+                                               return;
+                               }
+                       }
+                       int offset= fMethodDeclaration.getStartPosition() + fMethodDeclaration.getLength() - 1; // closing bracket
+                       fResult.add(new OccurrenceLocation(offset, 1, 0, fExitDescription));
+               }
+       }
+
+       @Override
+       public boolean visit(TypeDeclaration node) {
+               // Don't dive into a local type.
+               return false;
+       }
+
+       @Override
+       public boolean visit(AnonymousClassDeclaration node) {
+               // Don't dive into a local type.
+               return false;
+       }
+
+       @Override
+       public boolean visit(AnnotationTypeDeclaration node) {
+               // Don't dive into a local type.
+               return false;
+       }
+
+       @Override
+       public boolean visit(EnumDeclaration node) {
+               // Don't dive into a local type.
+               return false;
+       }
+
+       @Override
+       public boolean visit(ReturnStatement node) {
+               fResult.add(new OccurrenceLocation(node.getStartPosition(), node.getLength(), 0, fExitDescription));
+               return super.visit(node);
+       }
+
+       @Override
+       public boolean visit(TryStatement node) {
+               int currentSize= fCaughtExceptions.size();
+               List<CatchClause> catchClauses= node.catchClauses();
+               for (Iterator<CatchClause> iter= catchClauses.iterator(); iter.hasNext();) {
+                       Type type= iter.next().getException().getType();
+                       if (type instanceof UnionType) {
+                               List<Type> types= ((UnionType) type).types();
+                               for (Iterator<Type> iterator= types.iterator(); iterator.hasNext();) {
+                                       addCaughtException(iterator.next());
+                               }
+                       } else {
+                               addCaughtException(type);
+                       }
+               }
+               node.getBody().accept(this);
+
+               if (node.getAST().apiLevel() >= AST.JLS4) {
+                       List<VariableDeclarationExpression> resources= node.resources();
+                       for (Iterator<VariableDeclarationExpression> iterator= resources.iterator(); iterator.hasNext();) {
+                               iterator.next().accept(this);
+                       }
+
+                       //check if the method could exit as a result of resource#close()
+                       boolean exitMarked= false;
+                       for (VariableDeclarationExpression variable : resources) {
+                               Type type= variable.getType();
+                               IMethodBinding methodBinding= Bindings.findMethodInHierarchy(type.resolveBinding(), "close", new ITypeBinding[0]); //$NON-NLS-1$
+                               if (methodBinding != null) {
+                                       ITypeBinding[] exceptionTypes= methodBinding.getExceptionTypes();
+                                       for (int j= 0; j < exceptionTypes.length; j++) {
+                                               if (isExitPoint(exceptionTypes[j])) { // a close() throws an uncaught exception
+                                                       // mark name of resource
+                                                       for (VariableDeclarationFragment fragment : (List<VariableDeclarationFragment>) variable.fragments()) {
+                                                               SimpleName name= fragment.getName();
+                                                               fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fExitDescription));
+                                                       }
+                                                       if (!exitMarked) {
+                                                               // mark exit position
+                                                               exitMarked= true;
+                                                               Block body= node.getBody();
+                                                               int offset= body.getStartPosition() + body.getLength() - 1; // closing bracket of try block
+                                                               fResult.add(new OccurrenceLocation(offset, 1, 0, Messages.format(SearchMessages.MethodExitsFinder_occurrence_exit_impclict_close_description,
+                                                                               BasicElementLabels.getJavaElementName(fMethodDeclaration.getName().toString()))));
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               int toRemove= fCaughtExceptions.size() - currentSize;
+               for (int i= toRemove; i > 0; i--) {
+                       fCaughtExceptions.remove(currentSize);
+               }
+
+               // visit catch and finally
+               for (Iterator<CatchClause> iter= catchClauses.iterator(); iter.hasNext();) {
+                       iter.next().accept(this);
+               }
+               if (node.getFinally() != null)
+                       node.getFinally().accept(this);
+
+               // return false. We have visited the body by ourselves.
+               return false;
+       }
+
+       private void addCaughtException(Type type) {
+               ITypeBinding typeBinding= type.resolveBinding();
+               if (typeBinding != null) {
+                       fCaughtExceptions.add(typeBinding);
+               }
+       }
+
+       @Override
+       public boolean visit(ThrowStatement node) {
+               ITypeBinding exception= node.getExpression().resolveTypeBinding();
+               if (isExitPoint(exception)) {
+                       // mark 'throw'
+                       fResult.add(new OccurrenceLocation(node.getStartPosition(), 5, 0, fExitDescription));
+               }
+               return true;
+       }
+
+       @Override
+       public boolean visit(MethodInvocation node) {
+               if (isExitPoint(node.resolveMethodBinding())) {
+                       SimpleName name= node.getName();
+                       fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fExitDescription));
+               }
+               return true;
+       }
+
+       @Override
+       public boolean visit(SuperMethodInvocation node) {
+               if (isExitPoint(node.resolveMethodBinding())) {
+                       SimpleName name= node.getName();
+                       fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fExitDescription));
+               }
+               return true;
+       }
+
+       @Override
+       public boolean visit(ClassInstanceCreation node) {
+               if (isExitPoint(node.resolveConstructorBinding())) {
+                       Type name= node.getType();
+                       fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fExitDescription));
+               }
+               return true;
+       }
+
+       @Override
+       public boolean visit(ConstructorInvocation node) {
+               if (isExitPoint(node.resolveConstructorBinding())) {
+                       // mark 'this'
+                       fResult.add(new OccurrenceLocation(node.getStartPosition(), 4, 0, fExitDescription));
+               }
+               return true;
+       }
+
+       @Override
+       public boolean visit(SuperConstructorInvocation node) {
+               if (isExitPoint(node.resolveConstructorBinding())) {
+                       // mark 'super'
+                       fResult.add(new OccurrenceLocation(node.getStartPosition(), 5, 0, fExitDescription));
+               }
+               return true;
+       }
+
+       private boolean isExitPoint(ITypeBinding binding) {
+               if (binding == null)
+                       return false;
+               return !isCaught(binding);
+       }
+
+       private boolean isExitPoint(IMethodBinding binding) {
+               if (binding == null)
+                       return false;
+               ITypeBinding[] exceptions= binding.getExceptionTypes();
+               for (int i= 0; i < exceptions.length; i++) {
+                       if (!isCaught(exceptions[i]))
+                               return true;
+               }
+               return false;
+       }
+
+       private boolean isCaught(ITypeBinding binding) {
+               for (Iterator<ITypeBinding> iter= fCaughtExceptions.iterator(); iter.hasNext();) {
+                       ITypeBinding catchException= iter.next();
+                       if (catches(catchException, binding))
+                               return true;
+               }
+               return false;
+       }
+
+       private boolean catches(ITypeBinding catchTypeBinding, ITypeBinding throwTypeBinding) {
+               while(throwTypeBinding != null) {
+                       if (throwTypeBinding == catchTypeBinding)
+                               return true;
+                       throwTypeBinding= throwTypeBinding.getSuperclass();
+               }
+               return false;
+       }
+
+       public CompilationUnit getASTRoot() {
+               return fASTRoot;
+       }
+
+       public String getElementName() {
+               return fMethodDeclaration.getName().getIdentifier();
+       }
+
+       public String getID() {
+               return ID;
+       }
+
+       public String getJobLabel() {
+               return SearchMessages.MethodExitsFinder_job_label;
+       }
+
+       public int getSearchKind() {
+               return IOccurrencesFinder.K_BREAK_TARGET_OCCURRENCE;
+       }
+
+       public String getUnformattedPluralLabel() {
+               return SearchMessages.MethodExitsFinder_label_plural;
+       }
+
+       public String getUnformattedSingularLabel() {
+               return SearchMessages.MethodExitsFinder_label_singular;
+       }
+
+}