Index: core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java,v retrieving revision 1.59 diff -u -r1.59 ASTNodes.java --- core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java 22 Mar 2004 14:54:37 -0000 1.59 +++ core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java 22 Mar 2004 17:40:13 -0000 @@ -737,4 +737,36 @@ public static int changeVisibility(int modifiers, int visibility) { return (modifiers & CLEAR_VISIBILITY) | visibility; } + + + public static boolean isInvocation(Expression expression) { + int type= expression.getNodeType(); + return type == ASTNode.METHOD_INVOCATION || type == ASTNode.SUPER_METHOD_INVOCATION; + } + + public static boolean isLoopStatement(ASTNode node) { + int nodeType= node.getNodeType(); + return nodeType == ASTNode.FOR_STATEMENT || + nodeType == ASTNode.WHILE_STATEMENT || nodeType == ASTNode.DO_STATEMENT; + } + + public static boolean isControlStatement(ASTNode node) { + int nodeType= node.getNodeType(); + return nodeType == ASTNode.IF_STATEMENT || isLoopStatement(node); + } + + public static Statement getLoopBody(ASTNode node) { + int nodeType= node.getNodeType(); + switch(nodeType) { + case ASTNode.FOR_STATEMENT: + return ((ForStatement)node).getBody(); + case ASTNode.WHILE_STATEMENT: + return ((WhileStatement)node).getBody(); + case ASTNode.DO_STATEMENT: + return ((DoStatement)node).getBody(); + default: + Assert.isTrue(false, "Should not happen"); //$NON-NLS-1$ + } + return null; + } } Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/refactoring.properties =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/refactoring.properties,v retrieving revision 1.126 diff -u -r1.126 refactoring.properties --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/refactoring.properties 15 Mar 2004 17:10:43 -0000 1.126 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/refactoring.properties 22 Mar 2004 17:40:20 -0000 @@ -163,14 +163,14 @@ InlineMethodRefactoring.SourceAnalyzer.abstract_methods=Cannot inline abstract methods. CallInliner.receiver_type=Can\'t determine receiver\'s type. -CallInliner.execution_flow=Can\'t inline method. Return statement in method declaration interrupts execution flow. -CallInliner.multiDeclaration=Can\'t inline method used as an initializer in a multi fragment variable declaration. CallInliner.simple_functions=Inlining is only possible on simple functions (consisting of a single return statement), or functions used in an assignment. CallInliner.field_initializer_simple=In field initializers inlining is only supported for simple functions (e.g. functions consisting of a single return statement). -CallInliner.field_initialize_new_local=Can\'t inline field initializer because new local variable is required. CallInliner.field_initialize_write_parameter=Can\'t inline field initializer because one of the method parameters is used as an assignment target and will require new local variable. CallInliner.field_initialize_self_reference=Can\'t inline method. Method references the field to be initialized. CallInliner.constructors=Can\'t inline a constructor invocation that is used as a class instance creation. +CallInliner.return_in_loop=Can't inline a method that has a return statement inside a loop. +CallInliner.return_in_switch=Can't inline a method having a return statement inside a switch. +CallInliner.partial_return=Can't inline a method if not all execution paths have return statements. #-- SEF ------------------------------------------------------ SelfEncapsulateField.AccessAnalyzer.encapsulate_read_access=Encapsulate read access Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/CallContext.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/CallContext.java,v retrieving revision 1.8 diff -u -r1.8 CallContext.java --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/CallContext.java 10 Sep 2003 15:14:43 -0000 1.8 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/CallContext.java 22 Mar 2004 17:40:22 -0000 @@ -19,13 +19,11 @@ public String receiver; public boolean receiverIsStatic; public CodeScopeBuilder.Scope scope; - public int callMode; public ImportRewrite importer; - - public CallContext(CodeScopeBuilder.Scope s, int cm, ImportRewrite i) { + + public CallContext(CodeScopeBuilder.Scope s, ImportRewrite i) { super(); scope= s; - callMode= cm; importer= i; } } Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/CallInliner.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/CallInliner.java,v retrieving revision 1.43 diff -u -r1.43 CallInliner.java --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/CallInliner.java 16 Mar 2004 08:52:41 -0000 1.43 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/CallInliner.java 22 Mar 2004 17:40:23 -0000 @@ -26,21 +26,24 @@ import java.util.List; import org.eclipse.text.edits.MultiTextEdit; -import org.eclipse.text.edits.TextEdit; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.resources.IFile; import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ArrayCreation; +import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.CastExpression; +import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.DoStatement; import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.ExpressionStatement; import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.ForStatement; @@ -54,11 +57,15 @@ import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.ParenthesizedExpression; +import org.eclipse.jdt.core.dom.PrimitiveType; +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.SuperFieldAccess; +import org.eclipse.jdt.core.dom.SuperMethodInvocation; +import org.eclipse.jdt.core.dom.SwitchStatement; import org.eclipse.jdt.core.dom.ThisExpression; -import org.eclipse.jdt.core.dom.VariableDeclaration; +import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.jdt.core.dom.WhileStatement; @@ -89,7 +96,7 @@ import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry; public class CallInliner { - + private ICompilationUnit fCUnit; private ImportRewrite fImportEdit; private TextBuffer fBuffer; @@ -101,17 +108,18 @@ private ASTNode fInvocation; private ASTRewrite fRewriter; - private List fStatements; - private int fInsertionIndex; - private boolean fNeedsStatement; - private ASTNode fTargetNode; private FlowContext fFlowContext; private FlowInfo fFlowInfo; private CodeScopeBuilder.Scope fInvocationScope; - private boolean fFieldInitializer; - private List fLocals; private CallContext fContext; - + + private List fStatementsBefore; + private List fStatementReplacement; + private List fStatementsAfter; + private Expression fInvocationReplacement; + private boolean fRemoveInvocation; + private Statement fInvocationStatement; + private class InlineEvaluator extends HierarchicalASTVisitor { private ParameterData fFormalArgument; private boolean fResult; @@ -216,7 +224,7 @@ fBuffer= TextBuffer.acquire(getFile(fCUnit)); fSourceProvider= provider; fImportEdit= new ImportRewrite(fCUnit, settings); - fLocals= new ArrayList(3); + fStatementsBefore= new ArrayList(); } public void dispose() { @@ -231,10 +239,6 @@ return fImportEdit; } - public ASTNode getTargetNode() { - return fTargetNode; - } - public void initialize(BodyDeclaration declaration) { fBodyDeclaration= declaration; fRootScope= CodeScopeBuilder.perform(declaration, fSourceProvider.getDeclaration().resolveBinding()); @@ -250,17 +254,15 @@ public RefactoringStatus initialize(ASTNode invocation, int severity) { RefactoringStatus result= new RefactoringStatus(); fInvocation= invocation; - fLocals= new ArrayList(3); checkMethodDeclaration(result, severity); if (result.getSeverity() >= severity) return result; initializeRewriter(); - initializeTargetNode(); flowAnalysis(); - fContext= new CallContext(fInvocationScope, fTargetNode.getNodeType(), fImportEdit); + fContext= new CallContext(fInvocationScope, fImportEdit); computeRealArguments(); computeReceiver(); @@ -277,7 +279,6 @@ ASTNode parentField= ASTNodes.getParent(fInvocation, ASTNode.FIELD_DECLARATION); if(parentField != null) { fRewriter= new ASTRewrite(parentField); - fFieldInitializer= true; } else { ASTNode parentBlock= ASTNodes.getParent(fInvocation, ASTNode.BLOCK); @@ -286,16 +287,6 @@ } } - private void initializeTargetNode() { - ASTNode parent= fInvocation.getParent(); - int nodeType= parent.getNodeType(); - if (nodeType == ASTNode.EXPRESSION_STATEMENT || nodeType == ASTNode.RETURN_STATEMENT) { - fTargetNode= parent; - } else { - fTargetNode= fInvocation; - } - } - // the checks depend on invocation context and therefore can't be done in SourceAnalyzer private void checkMethodDeclaration(RefactoringStatus result, int severity) { MethodDeclaration methodDeclaration= fSourceProvider.getDeclaration(); @@ -304,20 +295,12 @@ if (fInvocation.getNodeType() != ASTNode.CONSTRUCTOR_INVOCATION && methodDeclaration.isConstructor()) { result.addEntry(new RefactoringStatusEntry( severity, //$NON-NLS-1$ - RefactoringCoreMessages.getString("CallInliner.constructors"), JavaStatusContext.create(fCUnit, fInvocation))); + RefactoringCoreMessages.getString("CallInliner.constructors"), JavaStatusContext.create(fCUnit, fInvocation))); //$NON-NLS-1$ } } private void checkInvocationContext(RefactoringStatus result, int severity) { if (ASTNodes.getParent(fInvocation, FieldDeclaration.class) != null) { - // it is allowed to inline a method used for field initialization - // if only it consists of single return statement - if(fSourceProvider.getNumberOfStatements() > 1) { - addEntry(result, - RefactoringCoreMessages.getString("CallInliner.field_initializer_simple"), //$NON-NLS-1$ - RefactoringStatusCodes.INLINE_METHOD_FIELD_INITIALIZER, severity); - return; - } int argumentsCount= fContext.arguments.length; for (int i= 0; i < argumentsCount; i++) { ParameterData parameter= fSourceProvider.getParameterData(i); @@ -328,12 +311,6 @@ return; } } - if(fLocals.size() > 0) { - addEntry(result, - RefactoringCoreMessages.getString("CallInliner.field_initialize_new_local"), //$NON-NLS-1$ - RefactoringStatusCodes.INLINE_METHOD_FIELD_INITIALIZER, severity); - return; - } // verify that the field is not referenced by the initializer method VariableDeclarationFragment variable= (VariableDeclarationFragment)ASTNodes.getParent(fInvocation, ASTNode.VARIABLE_DECLARATION_FRAGMENT); if(fSourceProvider.isVariableReferenced(variable.resolveBinding())) { @@ -343,14 +320,6 @@ return; } } - if (fSourceProvider.isExecutionFlowInterrupted()) { - VariableDeclaration vDecl= (VariableDeclaration)ASTNodes.getParent(fInvocation, VariableDeclaration.class); - if (vDecl != null) { - addEntry(result, RefactoringCoreMessages.getString("CallInliner.execution_flow"), //$NON-NLS-1$ - RefactoringStatusCodes.INLINE_METHOD_LOCAL_INITIALIZER, severity); - return; - } - } if (fInvocation.getNodeType() == ASTNode.METHOD_INVOCATION) { Expression exp= ((MethodInvocation)fInvocation).getExpression(); if (exp != null && exp.resolveTypeBinding() == null) { @@ -359,59 +328,6 @@ return; } } - int nodeType= fTargetNode.getNodeType(); - if (nodeType == ASTNode.EXPRESSION_STATEMENT) { - if (fSourceProvider.isExecutionFlowInterrupted()) { - addEntry(result, RefactoringCoreMessages.getString("CallInliner.execution_flow"), //$NON-NLS-1$ - RefactoringStatusCodes.INLINE_METHOD_EXECUTION_FLOW, severity); - return; - } - } else if (nodeType == ASTNode.METHOD_INVOCATION) { - ASTNode parent= fTargetNode.getParent(); - if (parent.getNodeType() == ASTNode.ASSIGNMENT || isSingleDeclaration(parent)) { - // this is ok - } else if (isMultiDeclarationFragment(parent)) { - if (!fSourceProvider.isSimpleFunction()) { - addEntry(result, RefactoringCoreMessages.getString("CallInliner.multiDeclaration"), //$NON-NLS-1$ - RefactoringStatusCodes.INLINE_METHOD_INITIALIZER_IN_FRAGEMENT, severity); - return; - } - } else if (fSourceProvider.getNumberOfStatements() > 1 ) { - addEntry(result, RefactoringCoreMessages.getString("CallInliner.simple_functions"), //$NON-NLS-1$ - RefactoringStatusCodes.INLINE_METHOD_ONLY_SIMPLE_FUNCTIONS, severity); - return; - } else if (!fSourceProvider.isSimpleFunction()) { - addEntry(result, RefactoringCoreMessages.getString("CallInliner.execution_flow"), //$NON-NLS-1$ - RefactoringStatusCodes.INLINE_METHOD_EXECUTION_FLOW, severity); - return; - } - } - } - - private static boolean isMultiDeclarationFragment(ASTNode node) { - int nodeType= node.getNodeType(); - if (nodeType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) { - node= node.getParent(); - if (node.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT) { - VariableDeclarationStatement vs= (VariableDeclarationStatement)node; - return vs.fragments().size() > 1; - } - } - return false; - } - - private static boolean isSingleDeclaration(ASTNode node) { - int type= node.getNodeType(); - if (type == ASTNode.SINGLE_VARIABLE_DECLARATION) - return true; - if (type == ASTNode.VARIABLE_DECLARATION_FRAGMENT) { - node= node.getParent(); - if (node.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT) { - VariableDeclarationStatement vs= (VariableDeclarationStatement)node; - return vs.fragments().size() == 1; - } - } - return false; } private void addEntry(RefactoringStatus result, String message, int code, int severity) { @@ -423,8 +339,8 @@ } private void flowAnalysis() { - fInvocationScope= fRootScope.findScope(fTargetNode.getStartPosition(), fTargetNode.getLength()); - fInvocationScope.setCursor(fTargetNode.getStartPosition()); + fInvocationScope= fRootScope.findScope(fInvocation.getStartPosition(), fInvocation.getLength()); + fInvocationScope.setCursor(fInvocation.getStartPosition()); fFlowContext= new FlowContext(0, fNumberOfLocals + 1); fFlowContext.setConsiderAccessMode(true); fFlowContext.setComputeMode(FlowContext.ARGUMENTS); @@ -440,20 +356,21 @@ } } - public TextEdit perform() throws CoreException { + public MultiTextEdit perform(RefactoringStatus status) throws CoreException { - String[] blocks= fSourceProvider.getCodeBlocks(fContext); - if(!fFieldInitializer) { - initializeInsertionPoint(fSourceProvider.getNumberOfStatements() + fLocals.size()); - } + ASTRewrite sourceRewriter= fSourceProvider.prepareForInlining(fContext); + analyzeInvocationContext(sourceRewriter, status); - addNewLocals(); - replaceCall(blocks); + MultiTextEdit edit= null; + if(!status.hasFatalError()) { + replaceCall(); + + edit= new MultiTextEdit(); + fRewriter.rewriteNode(fBuffer, edit); + fRewriter.removeModifications(); + } - MultiTextEdit result= new MultiTextEdit(); - fRewriter.rewriteNode(fBuffer, result); - fRewriter.removeModifications(); - return result; + return edit; } private void computeRealArguments() { @@ -463,15 +380,15 @@ Expression expression= (Expression)arguments.get(i); ParameterData parameter= fSourceProvider.getParameterData(i); if (canInline(expression, parameter)) { - realArguments[i] = getContent(expression); + realArguments[i]= getContent(expression, fBuffer); // fixes bugs #35905, #38471 if(expression instanceof CastExpression || expression instanceof ArrayCreation) { - realArguments[i] = "(" + realArguments[i] + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + realArguments[i]= "(" + realArguments[i] + ")"; //$NON-NLS-1$ //$NON-NLS-2$ } } else { String name= fInvocationScope.createName(parameter.getName(), true); realArguments[i]= name; - fLocals.add(createLocalDeclaration( + fStatementsBefore.add(createVariableDeclaration( parameter.getTypeBinding(), name, (Expression)fRewriter.createCopy(expression))); } @@ -494,7 +411,7 @@ case 0: // Make sure we evaluate the current receiver. Best is to assign to // local. - fLocals.add(createLocalDeclaration( + fStatementsBefore.add(createVariableDeclaration( receiver.resolveTypeBinding(), fInvocationScope.createName("r", true), //$NON-NLS-1$ (Expression)fRewriter.createCopy(receiver))); @@ -504,7 +421,7 @@ return; default: String local= fInvocationScope.createName("r", true); //$NON-NLS-1$ - fLocals.add(createLocalDeclaration( + fStatementsBefore.add(createVariableDeclaration( receiver.resolveTypeBinding(), local, (Expression)fRewriter.createCopy(receiver))); @@ -513,111 +430,707 @@ } } - private void addNewLocals() { - for (Iterator iter= fLocals.iterator(); iter.hasNext();) { - ASTNode element= (ASTNode)iter.next(); - fRewriter.markAsInserted(element); - fStatements.add(fInsertionIndex++, element); + private boolean canInline(Expression actualParameter, ParameterData formalParameter) { + InlineEvaluator evaluator= new InlineEvaluator(formalParameter); + actualParameter.accept(evaluator); + return evaluator.getResult(); + } + + private void analyzeInvocationContext(ASTRewrite sourceRewriter, RefactoringStatus status) throws CoreException { + + fInvocationStatement = (Statement)ASTNodes.getParent(fInvocation, Statement.class); + // direct invocation parent, not necessarily a statement + ASTNode invocationParent= fInvocation.getParent(); + int invocationParentType= invocationParent.getNodeType(); + + // order of following if statements matters + if (invocationParentType == ASTNode.RETURN_STATEMENT) { + // if method invocation is used as a return expression it can be inlined always + List statements= new ArrayList(fSourceProvider.getDeclaration().getBody().statements()); + List blocks= fSourceProvider.getSourceContent(statements); + fStatementReplacement= createPlaceholders(blocks, fRewriter, ASTRewrite.STATEMENT); + } + else { + if(verifySourceFlow(status)); + else if(fInvocationStatement != null && fInvocationStatement.getNodeType() == ASTNode.FOR_STATEMENT) { + inlineInForStatement(status, sourceRewriter); + } + else if(fInvocationStatement != null && fInvocationStatement.getNodeType() == ASTNode.WHILE_STATEMENT) { + inlineInLoopCondition(status, sourceRewriter); + } + else if(fInvocationStatement != null && fInvocationStatement.getNodeType() == ASTNode.DO_STATEMENT) { + inlineInLoopCondition(status, sourceRewriter); + } + else if (!fSourceProvider.hasReturnValue()) { + inlineVoidMethod(sourceRewriter, invocationParentType); + } + else if (invocationParentType == ASTNode.EXPRESSION_STATEMENT) { + inlineInExpressionStatement(sourceRewriter); + } + else if(invocationParentType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) { + inlineInVariableDeclaration(status, sourceRewriter); + } + else { + // covers not only expressions, but also all other statements except loops: if, assert etc. + inlineInExpression(sourceRewriter); + } } } - private void replaceCall(String[] blocks) throws CoreException { - // Inline empty body - if (blocks.length == 0) { - if (fNeedsStatement) { - fRewriter.markAsReplaced(fTargetNode, fTargetNode.getAST().newEmptyStatement(), null); - } else { - fRewriter.markAsRemoved(fTargetNode, null); - } + /** + * @param status refactoring status filled with detailed error information + * @return true if problem was encoutered otherwise false + */ + private boolean verifySourceFlow(RefactoringStatus status) { + FlowInfo sourceFlow= fSourceProvider.getFlowInfo(); + if(sourceFlow.hasReturningControlStatement(FlowInfo.LOOP_STATEMENTS)) { + status.addFatalError( + RefactoringCoreMessages.getString("CallInliner.return_in_loop"), //$NON-NLS-1$ + JavaStatusContext.create(fCUnit, fInvocation)); + } + else if(sourceFlow.hasReturningControlStatement(FlowInfo.SWITCH_STATEMENT)) { + status.addFatalError( + RefactoringCoreMessages.getString("CallInliner.return_in_switch"), //$NON-NLS-1$ + JavaStatusContext.create(fCUnit, fInvocation)); + } + else if(sourceFlow.hasAnyPartialReturn()) { + status.addFatalError( + RefactoringCoreMessages.getString("CallInliner.partial_return"), //$NON-NLS-1$ + JavaStatusContext.create(fCUnit, fInvocation)); + } + return status.hasFatalError(); + } + + /** + * Inlines a method invoked from loop condition checking. + * + * Invocation examples: while(inlineMe()); while {} do(inlineMe() == 10); for(; + * inlineMe().size() < 10; ); + * + * @param sourceRewriter source rewriter + * @throws CoreException + */ + private void inlineInLoopCondition(RefactoringStatus result, ASTRewrite sourceRewriter) throws CoreException { + + List statements= new ArrayList(fSourceProvider.getDeclaration().getBody().statements()); + + if (fSourceProvider.isSimpleFunction()) { + // if inlined function is simple - consists of a sinlge return statement, + // then it will be inlined directly into invocation place + List returnStatements= fSourceProvider.getReturnStatements(); + ReturnStatement rs= (ReturnStatement)returnStatements.get(0); + statements.clear(); + statements.add(rs.getExpression()); + List blocks= fSourceProvider.getSourceContent(statements); + List placeholders= createPlaceholders(blocks, fRewriter, ASTRewrite.EXPRESSION); + fInvocationReplacement= (Expression)placeholders.get(0); } else { - ASTNode node= null; - for (int i= 0; i < blocks.length - 1; i++) { - node= fRewriter.createPlaceholder(blocks[i], ASTRewrite.STATEMENT); - fRewriter.markAsInserted(node); - fStatements.add(fInsertionIndex++, node); - } - String block= blocks[blocks.length - 1]; - // We can inline a call where the declaration is a function and the call itself - // is a statement. In this case we have to create a temporary variable if the - // returned expression must be evaluated. - if (fContext.callMode == ASTNode.EXPRESSION_STATEMENT && fSourceProvider.hasReturnValue()) { - if (fSourceProvider.mustEvaluateReturnedExpression()) { - if (fSourceProvider.returnValueNeedsLocalVariable()) { - node= createLocalDeclaration( - fSourceProvider.getReturnType(), - fInvocationScope.createName(fSourceProvider.getMethodName(), true), - (Expression)fRewriter.createPlaceholder(block, ASTRewrite.EXPRESSION)); + addEntry(result, RefactoringCoreMessages.getString("CallInliner.simple_functions"), //$NON-NLS-1$ + RefactoringStatusCodes.INLINE_METHOD_ONLY_SIMPLE_FUNCTIONS, RefactoringStatus.FATAL); + } + } + + /** + * Inlines methods invoked from for statement. + * + * Invocation examples: for(; inlineMe().size() < 10; ); for(int i= inlineMe; true; ); + * for(; true; inlineMe()); + * + * @param result + * @param sourceRewriter + * @throws CoreException + */ + private void inlineInForStatement(RefactoringStatus result, ASTRewrite sourceRewriter) throws CoreException { + + ASTNode invocationParent= fInvocation.getParent(); + ForStatement parentStatement= (ForStatement)fInvocationStatement; + if (isChild(fInvocation, parentStatement.initializers())) { + int invocationParentType= invocationParent.getNodeType(); + if (invocationParent instanceof Expression) { + inlineInExpression(sourceRewriter); + } else if (invocationParentType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) { + inlineInForLoopVariableDeclaration(sourceRewriter); + } else if (invocationParentType == ASTNode.FOR_STATEMENT) { + // copy statements because this list can be modified later in this method + List statements= new ArrayList(fSourceProvider.getDeclaration().getBody().statements()); + + VariableDeclarationStatement variableDeclaration= removeReturns(sourceRewriter, statements); + if (fSourceProvider.isSimpleFunction() && variableDeclaration == null) { + if (statements.size() == 1) { + statements.clear(); + List returnStatements= fSourceProvider.getReturnStatements(); + ReturnStatement returnStatement= (ReturnStatement)returnStatements.get(0); + statements.add(returnStatement.getExpression()); + List blocks= fSourceProvider.getSourceContent(statements); + fInvocationReplacement= (Expression)createPlaceholders(blocks, fRewriter, ASTRewrite.EXPRESSION).get(0); } else { - node= fTargetNode.getAST().newExpressionStatement( - (Expression)fRewriter.createPlaceholder(block, ASTRewrite.EXPRESSION)); + fRemoveInvocation= true; + } + } else { + List blocks= fSourceProvider.getSourceContent(statements); + fStatementsBefore= createPlaceholders(blocks, fRewriter, ASTRewrite.STATEMENT); + if (variableDeclaration != null) { + fStatementsBefore.add(0, variableDeclaration); } + fRemoveInvocation= true; + } + } + } else if (isChild(fInvocation, parentStatement.updaters())) { + int invocationParentType= invocationParent.getNodeType(); + if (invocationParent instanceof Expression) { + if (fSourceProvider.isSimpleFunction()) { + inlineInExpression(sourceRewriter); + Assert.isTrue(fStatementsBefore.size() == 0); } else { - node= null; + List statements= fSourceProvider.getDeclaration().getBody().statements(); + + VariableDeclarationStatement variableDeclaration= createResultVariable(fInvocation.getAST()); + VariableDeclarationFragment fragment= (VariableDeclarationFragment)variableDeclaration.fragments().get(0); + AST sourceAst= sourceRewriter.getAST(); + SimpleName variableName= (SimpleName)ASTNode.copySubtree(sourceAst, fragment.getName()); + replaceReturnsWithAssignments(sourceRewriter, variableName); + List blocks= fSourceProvider.getSourceContent(statements); + fStatementsAfter= createPlaceholders(blocks, fRewriter, ASTRewrite.STATEMENT); + fStatementsBefore.add(variableDeclaration); } - } else if (fTargetNode instanceof Expression) { - node= fRewriter.createPlaceholder(block, ASTRewrite.EXPRESSION); - - // fixes bug #24941 - if(needsExplicitCast()) { - AST ast= node.getAST(); - CastExpression castExpression= ast.newCastExpression(); - ITypeBinding returnType= fSourceProvider.getReturnType(); - fImportEdit.addImport(returnType); - castExpression.setType(ASTNodeFactory.newType(ast, returnType, false)); - castExpression.setExpression((Expression)node); - node= castExpression; - } - - if (needsParenthesis()) { - ParenthesizedExpression pExp= fTargetNode.getAST().newParenthesizedExpression(); - pExp.setExpression((Expression)node); - node= pExp; + } else if (invocationParentType == ASTNode.FOR_STATEMENT) { + // copy statements because this list can be modified later in this method + List statements= new ArrayList(fSourceProvider.getDeclaration().getBody().statements()); + + VariableDeclarationStatement variableDeclaration= removeReturns(sourceRewriter, statements); + if (fSourceProvider.isSimpleFunction() && variableDeclaration == null) { + if (statements.size() == 1) { + statements.clear(); + List returnStatements= fSourceProvider.getReturnStatements(); + ReturnStatement returnStatement= (ReturnStatement)returnStatements.get(0); + statements.add(returnStatement.getExpression()); + List blocks= fSourceProvider.getSourceContent(statements); + fInvocationReplacement= (Expression)createPlaceholders(blocks, fRewriter, ASTRewrite.EXPRESSION).get(0); + } else { + fRemoveInvocation= true; + } + } else { + List blocks= fSourceProvider.getSourceContent(statements); + fStatementsAfter= createPlaceholders(blocks, fRewriter, ASTRewrite.STATEMENT); + if (variableDeclaration != null) { + fStatementsBefore.add(0, variableDeclaration); + } + fRemoveInvocation= true; } - } else { - node= fRewriter.createPlaceholder(block, ASTRewrite.STATEMENT); } - - // Now replace the target node with the source node + } else { + inlineInLoopCondition(result, sourceRewriter); + } + } + + private void inlineInForLoopVariableDeclaration(ASTRewrite sourceRewriter) throws CoreException { + + List returnStatements= fSourceProvider.getReturnStatements(); + List statements= new ArrayList(fSourceProvider.getDeclaration().getBody().statements()); + + if (returnStatements.size() == 1 && isLastReturn(statements)) { + inlineInVariableDeclaration(sourceRewriter, statements); + } else { + // method invocation in variable declaration fragment can only be used as an + // initializer + VariableDeclarationStatement declarationStatement= replaceReturnsWithAssignments(sourceRewriter); + VariableDeclarationFragment fragment= (VariableDeclarationFragment)(declarationStatement.fragments().get(0)); + List blocks= fSourceProvider.getSourceContent(statements); + fStatementsBefore= createPlaceholders(blocks, fRewriter, ASTRewrite.STATEMENT); + fStatementsBefore.add(0, declarationStatement); + fInvocationReplacement= fragment.getName(); + } + } + + private void inlineInVariableDeclaration(RefactoringStatus result, ASTRewrite sourceRewriter) throws CoreException { + + List statements= new ArrayList(fSourceProvider.getDeclaration().getBody().statements()); + inlineInVariableDeclaration(sourceRewriter, statements); + + if (ASTNodes.getParent(fInvocation, FieldDeclaration.class) != null) { + // for field declarations only invocation replacement is allowed + int numberOfStatements= 0; + if (fStatementsBefore != null) { + numberOfStatements+= fStatementsBefore.size(); + } + if (fStatementsAfter != null) { + numberOfStatements+= fStatementsAfter.size(); + } + if (fStatementReplacement != null) { + numberOfStatements+= fStatementReplacement.size(); + } + if (numberOfStatements > 0) { + addEntry(result, RefactoringCoreMessages.getString("CallInliner.field_initializer_simple"), //$NON-NLS-1$ + RefactoringStatusCodes.INLINE_METHOD_FIELD_INITIALIZER, RefactoringStatus.FATAL); + } + } + } + + private void inlineInVariableDeclaration(ASTRewrite sourceRewriter, List statements) throws CoreException { + + List returnStatements= fSourceProvider.getReturnStatements(); + + if (returnStatements.size() == 1 && isLastReturn(statements)) { + int numberOfStatements= statements.size(); + List nodes= new ArrayList(statements); + ReturnStatement rs= (ReturnStatement)returnStatements.get(0); + int index= nodes.indexOf(rs); + if (index != -1) { + nodes.remove(index); + statements.remove(index); + --numberOfStatements; + } + nodes.add(rs.getExpression()); + List blocks= fSourceProvider.getSourceContent(nodes); + + fInvocationReplacement= (Expression)fRewriter.createPlaceholder(blocks.remove(numberOfStatements).toString(), + ASTRewrite.EXPRESSION); + fStatementsBefore= createPlaceholders(blocks, fRewriter, ASTRewrite.STATEMENT); + } else { + // method invocation in variable declaration fragment can only be used as an + // initializer + AST sourceAST= sourceRewriter.getAST(); + ASTNode invocationParent= fInvocation.getParent(); + VariableDeclarationFragment fragment= (VariableDeclarationFragment)invocationParent; + fInvocationReplacement= createDefaultInitializer(sourceAST, resolveInvocationTypeBinding()); + SimpleName variable= fragment.getName(); + replaceReturnsWithAssignments(sourceRewriter, variable); + + List blocks= fSourceProvider.getSourceContent(statements); + fStatementsAfter= createPlaceholders(blocks, fRewriter, ASTRewrite.STATEMENT); + } + } + + private void inlineInExpression(ASTRewrite sourceRewriter) throws CoreException { + + List returnStatements= fSourceProvider.getReturnStatements(); + List statements= fSourceProvider.getDeclaration().getBody().statements(); + + if (returnStatements.size() == 1 && isLastReturn(statements)) { + AST sourceAst= sourceRewriter.getAST(); + ReturnStatement rs= (ReturnStatement)returnStatements.get(0); + Expression returnExpression= rs.getExpression(); + Expression node= null; + // fixes bug #24941 + if (needsExplicitCast(returnExpression)) { + CastExpression castExpression= sourceAst.newCastExpression(); + Type returnType= fSourceProvider.getDeclaration().getReturnType(); + Type type= createType(sourceAst, returnType.resolveBinding()); + castExpression.setType(type); + castExpression.setExpression((Expression)sourceRewriter.createCopy(returnExpression)); + node= castExpression; + } + if (needsParenthesis()) { + ParenthesizedExpression parenthesizedExpression= sourceAst.newParenthesizedExpression(); + if (node != null) { + parenthesizedExpression.setExpression(node); + } else { + parenthesizedExpression.setExpression((Expression)sourceRewriter.createCopy(returnExpression)); + } + node= parenthesizedExpression; + } if (node != null) { - if (fTargetNode == null) { - fRewriter.markAsInserted(node); - fStatements.add(fInsertionIndex++, node); + sourceRewriter.markAsReplaced(returnExpression, node, null); + } + // copying the list of statements to make a single call to getSourceContent + // method that will return source of all statements and return expression + List nodes= new ArrayList(statements); + // replace last return statement with its expression + nodes.set(nodes.size() - 1, returnExpression); + // get content of all statements and return expression in a single call + List blocks= fSourceProvider.getSourceContent(nodes); + // last string in the list corresponds to the only return expression + fInvocationReplacement= (Expression)fRewriter.createPlaceholder((String)blocks.remove(blocks.size() - 1), + ASTRewrite.EXPRESSION); + // all other strings are statements of the inlined method body + fStatementsBefore= createPlaceholders(blocks, fRewriter, ASTRewrite.STATEMENT); + } else { + VariableDeclarationStatement variableDeclaration= replaceReturnsWithAssignments(sourceRewriter); + + List blocks= fSourceProvider.getSourceContent(statements); + fStatementsBefore= createPlaceholders(blocks, fRewriter, ASTRewrite.STATEMENT); + fStatementsBefore.add(0, variableDeclaration); + + VariableDeclarationFragment fragment= (VariableDeclarationFragment)variableDeclaration.fragments().get(0); + fInvocationReplacement= fragment.getName(); + } + } + + private void inlineInExpressionStatement(ASTRewrite sourceRewriter) throws CoreException { + + List statements= new ArrayList(fSourceProvider.getDeclaration().getBody().statements()); + + VariableDeclarationStatement variableDeclaration= removeReturns(sourceRewriter, statements); + + List blocks= fSourceProvider.getSourceContent(statements); + fStatementReplacement= createPlaceholders(blocks, fRewriter, ASTRewrite.STATEMENT); + if (variableDeclaration != null) { + fStatementReplacement.add(0, variableDeclaration); + } + } + + /** + * Inlines a method with no return value. + * + * @param sourceRewriter source method rewriter + * @param invocationParentType type of direct invocation parent + * @throws CoreException + */ + private void inlineVoidMethod(ASTRewrite sourceRewriter, int invocationParentType) throws CoreException { + + List returnStatements= fSourceProvider.getReturnStatements(); + List statements= fSourceProvider.getDeclaration().getBody().statements(); + + for (Iterator it= returnStatements.iterator(); it.hasNext();) { + ReturnStatement rs= (ReturnStatement)it.next(); + ASTNode parent= rs.getParent(); + // if parent of return statement is a control statements(if, for, do, while) + // then empty statement is required in place of the return + if (parent != null && ASTNodes.isControlStatement(parent)) { + AST ast= fInvocation.getAST(); + sourceRewriter.markAsReplaced(rs, ast.newEmptyStatement(), null); + } else { + // otheriwse it is safe to remove return statement completely + sourceRewriter.markAsRemoved(rs, null); + statements.remove(rs); + } + } + + List blocks= fSourceProvider.getSourceContent(statements); + List placeholders= createPlaceholders(blocks, fRewriter, ASTRewrite.STATEMENT); + if (placeholders.size() > 0) { + if (invocationParentType == ASTNode.EXPRESSION_STATEMENT) { + fStatementReplacement= placeholders; + } else if (fInvocation.getNodeType() == ASTNode.CONSTRUCTOR_INVOCATION) { + fStatementReplacement= placeholders; + } else { + fStatementsBefore= placeholders; + fRemoveInvocation= true; + } + } else { + fRemoveInvocation= true; + } + } + + private VariableDeclarationStatement removeReturns(ASTRewrite sourceRewriter, List statements) { + + VariableDeclarationStatement variableDeclaration= null; + SimpleName variableName= null; + AST sourceAst= sourceRewriter.getAST(); + List returnStatements= fSourceProvider.getReturnStatements(); + for (Iterator it= returnStatements.iterator(); it.hasNext();) { + ReturnStatement returnStatement= (ReturnStatement)it.next(); + Expression returnExpression= returnStatement.getExpression(); + // check if return expression must be evaluated + if (ASTNodes.isLiteral(returnExpression) || returnExpression instanceof Name) { + ASTNode parent= returnStatement.getParent(); + // if parent of return statement is a control statements(if, for, do, + // while) + // then empty statement is required in place of the return + if (parent != null && ASTNodes.isControlStatement(parent)) { + sourceRewriter.markAsReplaced(returnStatement, sourceAst.newEmptyStatement(), null); } else { - fRewriter.markAsReplaced(fTargetNode, node, null); + // otheriwse it is safe to remove return statement completely + sourceRewriter.markAsRemoved(returnStatement, null); + statements.remove(returnStatement); } } else { - if (fTargetNode != null) { - fRewriter.markAsRemoved(fTargetNode, null); + // check if return expression needs a local variable + if (ASTNodes.isInvocation(returnExpression) || returnExpression instanceof ClassInstanceCreation) { + ExpressionStatement statement= sourceAst.newExpressionStatement((Expression)sourceRewriter + .createCopy(returnExpression)); + sourceRewriter.markAsReplaced(returnStatement, statement, null); + } else { + if (variableName == null) { + variableDeclaration= createResultVariable(fInvocation.getAST()); + VariableDeclarationFragment variableFragment= (VariableDeclarationFragment)variableDeclaration + .fragments().get(0); + variableName= (SimpleName)ASTNode.copySubtree(sourceAst, variableFragment.getName()); + } + replaceReturnWithAssignment(sourceRewriter, variableName, returnStatement); + } + } + } + return variableDeclaration; + } + + /** + * The method replaces every return statement with assignment to local variable. + * + * @param sourceRewriter rewriter to be used for the replacement + * @return + */ + private VariableDeclarationStatement replaceReturnsWithAssignments(ASTRewrite sourceRewriter) { + VariableDeclarationStatement variableDeclaration= createResultVariable(fInvocation.getAST()); + VariableDeclarationFragment fragment= (VariableDeclarationFragment)variableDeclaration.fragments().get(0); + AST sourceAst= sourceRewriter.getRootNode().getAST(); + SimpleName variableName= (SimpleName)ASTNode.copySubtree(sourceAst, fragment.getName()); + replaceReturnsWithAssignments(sourceRewriter, variableName); + return variableDeclaration; + } + + private void replaceReturnsWithAssignments(ASTRewrite sourceRewriter, SimpleName variable) { + List returnStatements= fSourceProvider.getReturnStatements(); + for (Iterator it= returnStatements.iterator(); it.hasNext();) { + ReturnStatement returnStatement= (ReturnStatement)it.next(); + // every return is substituted with an assignement to the variable. + // prior to that method initializeTarget has replaced variable + // initializer with a default value (0 or null). + replaceReturnWithAssignment(sourceRewriter, variable, returnStatement); + } + } + + private void replaceReturnWithAssignment(ASTRewrite sourceRewriter, SimpleName variable, ReturnStatement returnStatement) { + AST ast= sourceRewriter.getAST(); + Expression returnExpression= returnStatement.getExpression(); + Assignment assignment= ast.newAssignment(); + assignment.setLeftHandSide((Expression)ASTNode.copySubtree(ast, variable)); + assignment.setRightHandSide((Expression)sourceRewriter.createCopy(returnExpression)); + sourceRewriter.markAsReplaced(returnStatement, ast.newExpressionStatement(assignment), null); + } + + private Expression createDefaultInitializer(AST ast, ITypeBinding typeBinding) { + if(typeBinding.isPrimitive()) { + if(typeBinding.getName().equals(PrimitiveType.BOOLEAN.toString())) { + return ast.newBooleanLiteral(false); + } + else { + return ast.newNumberLiteral(); + } + } + return ast.newNullLiteral(); + } + + private List initializeInsertionPoint(Statement invocationStatement, boolean before, boolean replacement, boolean after) { + List statements= null; + ASTNode container= invocationStatement.getParent(); + int type= container.getNodeType(); + if (type == ASTNode.BLOCK) { + statements= ((Block)container).statements(); + } else if (type == ASTNode.SWITCH_STATEMENT) { + statements= ((SwitchStatement)container).statements(); + } else if (ASTNodes.isControlStatement(container)) { + int numberOfStatements= 0; + if(before && fStatementsBefore != null) { + numberOfStatements+= fStatementsBefore.size(); + } + if(after && fStatementsAfter != null) { + numberOfStatements+= fStatementsAfter.size(); + } + if(replacement) { + if(fStatementReplacement != null) { + numberOfStatements+= fStatementReplacement.size(); + } + else { + ++numberOfStatements; + } + } + if (numberOfStatements > 1) { + Block block= fInvocation.getAST().newBlock(); + statements= block.statements(); + Statement currentStatement= null; + switch(type) { + case ASTNode.FOR_STATEMENT: + currentStatement= ((ForStatement)container).getBody(); + break; + case ASTNode.WHILE_STATEMENT: + currentStatement= ((WhileStatement)container).getBody(); + break; + case ASTNode.DO_STATEMENT: + currentStatement= ((DoStatement)container).getBody(); + break; + case ASTNode.IF_STATEMENT: + IfStatement node= (IfStatement)container; + Statement thenPart= node.getThenStatement(); + if (ASTNodes.isParent(fInvocation, thenPart)) { + currentStatement= thenPart; + } else { + currentStatement= node.getElseStatement(); + } + break; + } + Assert.isNotNull(currentStatement); + if (replacement && fStatementReplacement == null) { + if (currentStatement == invocationStatement) { + if(invocationStatement.getNodeType() != ASTNode.EMPTY_STATEMENT) { + statements.add(fRewriter.createCopy(currentStatement)); + } + } + } + fRewriter.markAsReplaced(currentStatement, block, null); + } + } + return statements; + } + + private void replaceCall() { + + List statements= null; + Statement invocationStatement= null; + if(fInvocation instanceof Statement) { + // the case of constructor invocation + invocationStatement= (Statement)fInvocation; + } + else { + invocationStatement= (Statement)ASTNodes.getParent(fInvocation, Statement.class); + } + + int insertionIndex= 0; + if(invocationStatement != null) { + // 'after' statements are processed separately for loops + statements= initializeInsertionPoint(invocationStatement, true, true, + !ASTNodes.isLoopStatement(invocationStatement)); + if(statements != null) { + insertionIndex= statements.indexOf(invocationStatement); + if(insertionIndex == -1) { + insertionIndex= 0; + } + } + } + if (fStatementsBefore != null) { + insertionIndex= insertStatements(statements, + fStatementsBefore, insertionIndex); + } + if (fStatementReplacement != null) { + if(fStatementReplacement.size() == 0) { + fStatementReplacement= null; + fRemoveInvocation= true; + } + } + if (fStatementReplacement != null) { + if(!fStatementReplacement.isEmpty()) { + if(statements == null || statements.contains(invocationStatement)) { + ASTNode node= (ASTNode)fStatementReplacement.remove(0); + fRewriter.markAsReplaced(invocationStatement, node, null); + ++insertionIndex; + } + insertionIndex= insertStatements(statements, + fStatementReplacement, insertionIndex); + } + } + else if (fInvocationReplacement != null) { + fRewriter.markAsReplaced(fInvocation, fInvocationReplacement, null); + fInvocationReplacement= null; + insertionIndex++; + } + else if(fRemoveInvocation) { + ASTNode parent= fInvocation.getParent(); + if(parent.getNodeType() == ASTNode.EXPRESSION_STATEMENT) { + ASTNode parentParent= parent.getParent(); + if (ASTNodes.isControlStatement(parentParent)) { + // if parent of return statement is a control statements(if, for, do, while) + // then empty statement is required in place of the return + AST ast= fInvocation.getAST(); + fRewriter.markAsReplaced(parent, ast.newEmptyStatement(), null); + } else { + // otheriwse it is safe to remove return statement completely + fRewriter.markAsRemoved(parent, null); + } + } + else { + fRewriter.markAsRemoved(fInvocation, null); + } + } + if (fStatementsAfter != null) { + ASTNode replaceNode= null; + if(invocationStatement != null && ASTNodes.isLoopStatement(invocationStatement)) { + // 'after' statements for loops are processed separately; + // they are appended to the end of loop's body + Statement body= ASTNodes.getLoopBody(invocationStatement); + if(body.getNodeType() == ASTNode.BLOCK) { + Block block= (Block)body; + statements= block.statements(); + insertionIndex= statements.size(); + } + else { + statements= initializeInsertionPoint(body, false, false, true); + if(statements != null) { + insertionIndex= statements.size(); + } + else { + replaceNode= body; + } } } + if(statements != null) { + insertionIndex= insertStatements(statements, + fStatementsAfter, insertionIndex); + } + else { + fRewriter.markAsReplaced(replaceNode, (ASTNode)fStatementsAfter.get(0), null); + } + } + } + + private int insertStatements(List statements, List statementsToInsert, int index) { + for (Iterator it= statementsToInsert.iterator(); it.hasNext();) { + ASTNode node= (ASTNode)it.next(); + fRewriter.markAsInserted(node); + statements.add(index++, node); } + return index; + } + + private Type createType(AST ast, ITypeBinding typeBinding) { + String typeName= fImportEdit.addImport(typeBinding); + return ASTNodeFactory.newType(ast, typeName); + } + + /** + * Creates a local variable to keep result of the method invocation. + * + * @param ast + * @return + */ + private VariableDeclarationStatement createResultVariable(AST ast) { + return createResultVariable(createDefaultInitializer( + ast, fSourceProvider.getReturnType())); + } + + /** + * Creates a local variable to keep result of the method invocation. + * + * @param initializer variable initializer + * @return + */ + private VariableDeclarationStatement createResultVariable(Expression initializer) { + return createVariableDeclaration( + fSourceProvider.getReturnType(), + fRootScope.createName(fSourceProvider.getMethodName(), true), + initializer); + } + + private VariableDeclarationStatement createVariableDeclaration(ITypeBinding typeBinding, String name, Expression initializer) { + AST ast= initializer.getAST(); + Type type= createType(ast, typeBinding); + VariableDeclarationFragment fragment= ast.newVariableDeclarationFragment(); + fragment.setName(ast.newSimpleName(name)); + fragment.setInitializer(initializer); + VariableDeclarationStatement statement= ast.newVariableDeclarationStatement(fragment); + statement.setType(type); + return statement; } /** * @return true if explicit cast is needed otherwise false * @throws JavaModelException */ - private boolean needsExplicitCast() { + private boolean needsExplicitCast(Expression returnExpression) { // if the return type of the method is the same as the type of the // returned expression then we don't need an explicit cast. - if (fSourceProvider.returnTypeMatchesReturnExpressions()) - return false; - ASTNode parent= fTargetNode.getParent(); + ITypeBinding returnType= fSourceProvider.getReturnType(); + if (Bindings.equals(returnType, returnExpression.resolveTypeBinding())) + return false; + ASTNode parent= fInvocation.getParent(); int nodeType= parent.getNodeType(); if (nodeType == ASTNode.METHOD_INVOCATION) { MethodInvocation methodInvocation= (MethodInvocation)parent; - if(methodInvocation.getExpression() == fTargetNode) + if(methodInvocation.getExpression() == fInvocation) return false; IMethodBinding method= methodInvocation.resolveMethodBinding(); ITypeBinding[] parameters= method.getParameterTypes(); int argumentIndex= methodInvocation.arguments().indexOf(fInvocation); - List returnExprs= fSourceProvider.getReturnExpressions(); - // it is infered that only methods consisting of a single - // return statement can be inlined as parameters in other - // method invocations - if (returnExprs.size() != 1) - return false; - parameters[argumentIndex]= ((Expression)returnExprs.get(0)).resolveTypeBinding(); + parameters[argumentIndex]= returnExpression.resolveTypeBinding(); ITypeBinding type= ASTNodes.getReceiverTypeBinding(methodInvocation); TypeBindingVisitor visitor= new AmbiguousMethodAnalyzer(method, parameters); @@ -641,90 +1154,51 @@ private boolean needsParenthesis() { if (!fSourceProvider.needsReturnedExpressionParenthesis()) return false; - ASTNode parent= fTargetNode.getParent(); + ASTNode parent= fInvocation.getParent(); int type= parent.getNodeType(); - return type == ASTNode.METHOD_INVOCATION || (parent instanceof Expression && type != ASTNode.ASSIGNMENT); - } - - private VariableDeclarationStatement createLocalDeclaration(ITypeBinding type, String name, Expression initializer) { - String typeName= fImportEdit.addImport(type); - VariableDeclarationStatement decl= (VariableDeclarationStatement)ASTNodeFactory.newStatement( - fInvocation.getAST(), typeName + " " + name + ";"); //$NON-NLS-1$ //$NON-NLS-2$ - ((VariableDeclarationFragment)decl.fragments().get(0)).setInitializer(initializer); - return decl; + return type == ASTNode.METHOD_INVOCATION || + (parent instanceof Expression && type != ASTNode.ASSIGNMENT); } - private boolean canInline(Expression actualParameter, ParameterData formalParameter) { - InlineEvaluator evaluator= new InlineEvaluator(formalParameter); - actualParameter.accept(evaluator); - return evaluator.getResult(); - } - - private void initializeInsertionPoint(int nos) { - fStatements= null; - fInsertionIndex= -1; - fNeedsStatement= false; - ASTNode parentStatement= ASTNodes.getParent(fInvocation, Statement.class); - ASTNode container= parentStatement.getParent(); - int type= container.getNodeType(); - if (type == ASTNode.BLOCK) { - fStatements= ((Block)container).statements(); - fInsertionIndex= fStatements.indexOf(parentStatement); - } else if (isControlStatement(container)) { - fNeedsStatement= true; - if (nos > 1) { - Block block= fInvocation.getAST().newBlock(); - fStatements= block.statements(); - fInsertionIndex= 0; - Statement currentStatement= null; - switch(type) { - case ASTNode.FOR_STATEMENT: - currentStatement= ((ForStatement)container).getBody(); - break; - case ASTNode.WHILE_STATEMENT: - currentStatement= ((WhileStatement)container).getBody(); - break; - case ASTNode.DO_STATEMENT: - currentStatement= ((DoStatement)container).getBody(); - break; - case ASTNode.IF_STATEMENT: - IfStatement node= (IfStatement)container; - Statement thenPart= node.getThenStatement(); - if (fTargetNode == thenPart || ASTNodes.isParent(fTargetNode, thenPart)) { - currentStatement= thenPart; - } else { - currentStatement= node.getElseStatement(); - } - break; - } - Assert.isNotNull(currentStatement); - // The method to be inlined is not the body of the control statement. - if (currentStatement != fTargetNode) { - ASTNode copy= fRewriter.createCopy(currentStatement); - fStatements.add(copy); - } else { - // We can't replace a copy with something else. So we - // have to insert all statements to be inlined. - fTargetNode= null; - } - fRewriter.markAsReplaced(currentStatement, block, null); + private static List createPlaceholders(List blocks, ASTRewrite rewriter, int nodeType) { + for (int i= 0; i < blocks.size(); i++) { + String block= blocks.get(i).toString(); + if(block != null) { + blocks.set(i, rewriter.createPlaceholder(block, nodeType)); } } - // We only insert one new statement or we delete the existing call. - // So there is no need to have an insertion index. + return blocks; + } + + private static String getContent(ASTNode node, TextBuffer buffer) { + return InlineMethodRefactoring.getContent(node.getStartPosition(), node.getLength(), buffer); } - private String getContent(ASTNode node) { - return fBuffer.getContent(node.getStartPosition(), node.getLength()); + private ITypeBinding resolveInvocationTypeBinding() { + if(fInvocation instanceof MethodInvocation) { + return ((MethodInvocation)fInvocation).resolveTypeBinding(); + } + else if(fInvocation instanceof SuperMethodInvocation) { + return ((SuperMethodInvocation)fInvocation).resolveTypeBinding(); + } + return null; } - private static IFile getFile(ICompilationUnit cu) throws CoreException { + private static IFile getFile(ICompilationUnit cu) { return (IFile)WorkingCopyUtil.getOriginal(cu).getResource(); } - - private boolean isControlStatement(ASTNode node) { - int type= node.getNodeType(); - return type == ASTNode.IF_STATEMENT || type == ASTNode.FOR_STATEMENT || - type == ASTNode.WHILE_STATEMENT || type == ASTNode.DO_STATEMENT; + + private static boolean isLastReturn(List statements) { + return statements.get(statements.size() - 1) instanceof ReturnStatement; + } + + private static boolean isChild(ASTNode node, List parents) { + for (Iterator iter = parents.iterator(); iter.hasNext();) { + ASTNode parent= (ASTNode) iter.next(); + if(parent == node || ASTNodes.isParent(node, parent)) { + return true; + } + } + return false; } } Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/InlineMethodRefactoring.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/InlineMethodRefactoring.java,v retrieving revision 1.49 diff -u -r1.49 InlineMethodRefactoring.java --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/InlineMethodRefactoring.java 19 Mar 2004 20:52:54 -0000 1.49 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/InlineMethodRefactoring.java 22 Mar 2004 17:40:24 -0000 @@ -57,8 +57,12 @@ import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext; import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange; import org.eclipse.jdt.internal.corext.refactoring.changes.ValidationStateChange; +import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager; +import org.eclipse.jdt.internal.corext.textmanipulation.TextBuffer; +import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil; import org.eclipse.jdt.internal.corext.util.JavaModelUtil; +import org.eclipse.jdt.internal.corext.util.Strings; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.Refactoring; import org.eclipse.ltk.core.refactoring.RefactoringStatus; @@ -218,8 +222,10 @@ if (result.hasFatalError()) break; if (result.getSeverity() < fTargetProvider.getStatusSeverity()) { + MultiTextEdit edit= inliner.perform(result); + if (result.hasFatalError()) + break; added= true; - TextEdit edit= inliner.perform(); change.addTextEditGroup( new TextEditGroup(RefactoringCoreMessages.getString("InlineMethodRefactoring.edit.inline"), new TextEdit[] { edit })); //$NON-NLS-1$ root.addChild(edit); @@ -282,20 +288,6 @@ return null; } - source= JavaModelUtil.toWorkingCopy(source); - - /*if (!source.isWorkingCopy()) { - // try to find a working copy if exists. - // XXX: This is a layer breaker - should not access jdt.ui - IWorkingCopy[] workingCopies= JavaUI.getSharedWorkingCopiesOnClasspath(); - for (int i= 0; i < workingCopies.length; i++) { - IWorkingCopy wcopy= workingCopies[i]; - if (source.equals(wcopy.getOriginalElement())) { - source= (ICompilationUnit)wcopy; - break; - } - } - }*/ declaration= (MethodDeclaration)JavaElementMapper.perform(method, MethodDeclaration.class); if (declaration != null) { return new SourceProvider(source, declaration); @@ -306,7 +298,7 @@ } private static ASTNode getTargetNode(ICompilationUnit unit, int offset, int length) { - CompilationUnit root= AST.parseCompilationUnit(unit, true); + CompilationUnit root= new RefactoringASTParser(AST.LEVEL_2_0).parse(unit, true); ASTNode node= null; try { node= checkNode(NodeFinder.perform(root, offset, length, unit)); @@ -395,6 +387,7 @@ "InlineMethodRefactoring.checking.implements.error", //$NON-NLS-1$ pm); } + private void checkTypes(RefactoringStatus result, IMethod method, IType[] types, String key, IProgressMonitor pm) { pm.beginTask("", types.length); //$NON-NLS-1$ for (int i= 0; i < types.length; i++) { @@ -407,4 +400,11 @@ } } } + + /* package*/ static String getContent(int offset, int length, TextBuffer buffer) { + String content= buffer.getContent(offset, length); + String lines[]= Strings.convertIntoLines(content); + Strings.trimIndentation(lines, CodeFormatterUtil.getTabWidth(), false); + return Strings.concatenate(lines, buffer.getLineDelimiter()); + } } Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/SourceAnalyzer.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/SourceAnalyzer.java,v retrieving revision 1.20 diff -u -r1.20 SourceAnalyzer.java --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/SourceAnalyzer.java 16 Mar 2004 08:52:41 -0000 1.20 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/SourceAnalyzer.java 22 Mar 2004 17:40:25 -0000 @@ -39,13 +39,11 @@ import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.QualifiedName; -import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.ThisExpression; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; - import org.eclipse.jdt.internal.corext.dom.ASTNodes; import org.eclipse.jdt.internal.corext.dom.LocalVariableIndex; import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; @@ -77,14 +75,7 @@ private class ActivationAnalyzer extends ASTVisitor { public RefactoringStatus status= new RefactoringStatus(); - private ASTNode fLastNode= getLastNode(); private IMethodBinding fBinding= getBinding(); - public boolean visit(ReturnStatement node) { - if (node != fLastNode) { - fInterruptedExecutionFlow= true; - } - return true; - } public boolean visit(TypeDeclaration node) { return false; } @@ -120,12 +111,6 @@ } return true; } - private ASTNode getLastNode() { - List statements= fDeclaration.getBody().statements(); - if (statements.size() == 0) - return null; - return (ASTNode)statements.get(statements.size() - 1); - } private IMethodBinding getBinding() { return fDeclaration.resolveBinding(); } @@ -244,8 +229,8 @@ private Map fNames; private List fImplicitReceivers; private List fTypes; - private boolean fInterruptedExecutionFlow; - + private FlowInfo fFlowInfo; + public SourceAnalyzer(ICompilationUnit unit, MethodDeclaration declaration) { super(); fCUnit= unit; @@ -261,10 +246,6 @@ fTypes= new ArrayList(2); } - public boolean isExecutionFlowInterrupted() { - return fInterruptedExecutionFlow; - } - public RefactoringStatus checkActivation() throws JavaModelException { RefactoringStatus result= new RefactoringStatus(); if (!fCUnit.isStructureKnown()) { @@ -289,9 +270,6 @@ ActivationAnalyzer analyzer= new ActivationAnalyzer(); fDeclaration.accept(analyzer); result.merge(analyzer.status); - if (!result.hasFatalError()) { - - } return result; } @@ -304,16 +282,20 @@ context.setConsiderAccessMode(true); context.setComputeMode(FlowContext.MERGE); InOutFlowAnalyzer flowAnalyzer= new InOutFlowAnalyzer(context); - FlowInfo info= flowAnalyzer.perform(getStatements()); + fFlowInfo= flowAnalyzer.perform(getStatements()); for (Iterator iter= fDeclaration.parameters().iterator(); iter.hasNext();) { SingleVariableDeclaration element= (SingleVariableDeclaration) iter.next(); IVariableBinding binding= element.resolveBinding(); ParameterData data= (ParameterData)element.getProperty(ParameterData.PROPERTY); - data.setAccessMode(info.getAccessMode(context, binding)); + data.setAccessMode(fFlowInfo.getAccessMode(context, binding)); } } + public FlowInfo getFlowInfo() { + return fFlowInfo; + } + public Collection getUsedNames() { return fNames.values(); } @@ -325,6 +307,7 @@ public List getUsedTypes() { return fTypes; } + private ASTNode[] getStatements() { List statements= fDeclaration.getBody().statements(); return (ASTNode[]) statements.toArray(new ASTNode[statements.size()]); Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/SourceProvider.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/SourceProvider.java,v retrieving revision 1.33 diff -u -r1.33 SourceProvider.java --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/SourceProvider.java 16 Mar 2004 08:52:41 -0000 1.33 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/SourceProvider.java 22 Mar 2004 17:40:25 -0000 @@ -22,6 +22,7 @@ import java.util.Iterator; import java.util.List; +import org.eclipse.text.edits.MalformedTreeException; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.RangeMarker; import org.eclipse.text.edits.TextEdit; @@ -33,6 +34,8 @@ import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; +import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.IMethodBinding; @@ -45,46 +48,47 @@ import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; - -import org.eclipse.jface.text.IRegion; -import org.eclipse.jface.text.Region; +import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.internal.corext.codemanipulation.ImportRewrite; import org.eclipse.jdt.internal.corext.dom.ASTNodes; import org.eclipse.jdt.internal.corext.dom.ASTRewrite; import org.eclipse.jdt.internal.corext.dom.Bindings; import org.eclipse.jdt.internal.corext.dom.CodeScopeBuilder; +import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowInfo; import org.eclipse.jdt.internal.corext.textmanipulation.TextBuffer; import org.eclipse.jdt.internal.corext.textmanipulation.TextBufferEditor; -import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil; -import org.eclipse.jdt.internal.corext.util.Strings; import org.eclipse.ltk.core.refactoring.RefactoringStatus; public class SourceProvider { + private static class ReturnCollector extends ASTVisitor { + private List fNodes; + public ReturnCollector() { + fNodes= new ArrayList(); + } + public List getNodes() { + return fNodes; + } + public boolean visit(ReturnStatement node) { + fNodes.add(node); + return false; + } + public boolean visit(AnonymousClassDeclaration node) { + return false; + } + public boolean visit(TypeDeclaration node) { + return false; + } + } + private ICompilationUnit fCUnit; private TextBuffer fBuffer; private MethodDeclaration fDeclaration; private ASTRewrite fRewriter; private SourceAnalyzer fAnalyzer; - private boolean fMustEvalReturnedExpression; - private boolean fReturnValueNeedsLocalVariable; - private List fReturnExpressions; + private List fReturnStatements; - private class ReturnAnalyzer extends ASTVisitor { - public boolean visit(ReturnStatement node) { - Expression expression= node.getExpression(); - if (!(ASTNodes.isLiteral(expression) || expression instanceof Name)) { - fMustEvalReturnedExpression= true; - } - if (Invocations.isInvocation(expression) || expression instanceof ClassInstanceCreation) { - fReturnValueNeedsLocalVariable= false; - } - fReturnExpressions.add(expression); - return false; - } - } - public SourceProvider(ICompilationUnit unit, MethodDeclaration declaration) { super(); fCUnit= unit; @@ -97,8 +101,6 @@ } fRewriter= new ASTRewrite(fDeclaration); fAnalyzer= new SourceAnalyzer(fCUnit, fDeclaration); - fReturnValueNeedsLocalVariable= true; - fReturnExpressions= new ArrayList(); } public RefactoringStatus checkActivation() throws JavaModelException { @@ -108,17 +110,11 @@ public void initialize() throws JavaModelException { fBuffer= TextBuffer.create(fCUnit.getBuffer().getContents()); fAnalyzer.analyzeParameters(); - if (hasReturnValue()) { - ASTNode last= getLastStatement(); - if (last != null) { - ReturnAnalyzer analyzer= new ReturnAnalyzer(); - last.accept(analyzer); - } - } - } - - public boolean isExecutionFlowInterrupted() { - return fAnalyzer.isExecutionFlowInterrupted(); + + Block methodBody= fDeclaration.getBody(); + ReturnCollector returnCollector = new ReturnCollector(); + methodBody.accept(returnCollector); + fReturnStatements= returnCollector.getNodes(); } static class VariableReferenceFinder extends ASTVisitor { @@ -155,14 +151,6 @@ return binding.getReturnType() != fDeclaration.getAST().resolveWellKnownType("void"); //$NON-NLS-1$ } - public boolean mustEvaluateReturnedExpression() { - return fMustEvalReturnedExpression; - } - - public boolean returnValueNeedsLocalVariable() { - return fReturnValueNeedsLocalVariable; - } - public int getNumberOfStatements() { return fDeclaration.getBody().statements().size(); } @@ -182,24 +170,18 @@ return fDeclaration.getName().getIdentifier(); } + public FlowInfo getFlowInfo() { + return fAnalyzer.getFlowInfo(); + } + public ITypeBinding getReturnType() { return fDeclaration.resolveBinding().getReturnType(); } - public List getReturnExpressions() { - return fReturnExpressions; + public List getReturnStatements() { + return fReturnStatements; } - - public boolean returnTypeMatchesReturnExpressions() { - ITypeBinding returnType= getReturnType(); - for (Iterator iter= fReturnExpressions.iterator(); iter.hasNext();) { - Expression expression= (Expression)iter.next(); - if (!Bindings.equals(returnType, expression.resolveTypeBinding())) - return false; - } - return true; - } - + public ParameterData getParameterData(int index) { SingleVariableDeclaration decl= (SingleVariableDeclaration)fDeclaration.parameters().get(index); return (ParameterData)decl.getProperty(ParameterData.PROPERTY); @@ -229,63 +211,14 @@ rewriter.removeModifications(); return result; } - - public String[] getCodeBlocks(CallContext context) throws CoreException { + + public ASTRewrite prepareForInlining(CallContext context) { replaceParameterWithExpression(context.arguments); updateImplicitReceivers(context); makeNamesUnique(context.scope); updateTypes(context); - List ranges= null; - if (hasReturnValue()) { - if (context.callMode == ASTNode.RETURN_STATEMENT) { - ranges= getStatementRanges(); - } else { - ranges= getExpressionRanges(); - } - } else { - ASTNode last= getLastStatement(); - if (last != null && last.getNodeType() == ASTNode.RETURN_STATEMENT) { - ranges= getReturnStatementRanges(); - } else { - ranges= getStatementRanges(); - } - } - - MultiTextEdit dummy= new MultiTextEdit(); - fRewriter.rewriteNode(fBuffer, dummy); - - int size= ranges.size(); - RangeMarker[] markers= new RangeMarker[size]; - for (int i= 0; i < markers.length; i++) { - IRegion range= (IRegion)ranges.get(i); - markers[i]= new RangeMarker(range.getOffset(), range.getLength()); - } - int split; - if (size <= 1) { - split= Integer.MAX_VALUE; - } else { - IRegion region= (IRegion)ranges.get(0); - split= region.getOffset() + region.getLength(); - } - TextEdit[] edits= dummy.removeChildren(); - for (int i= 0; i < edits.length; i++) { - TextEdit edit= edits[i]; - int pos= edit.getOffset() >= split ? 1 : 0; - markers[pos].addChild(edit); - } - MultiTextEdit root= new MultiTextEdit(); - root.addChildren(markers); - TextBufferEditor editor= new TextBufferEditor(fBuffer); - editor.add(root); - UndoEdit undo= editor.performEdits(null); - String[] result= getBlocks(markers); - // It is faster to undo the changes than coping the buffer over and over again. - TextBufferEditor undoEditor= new TextBufferEditor(fBuffer); - undoEditor.add(undo); - undoEditor.performEdits(null); - fRewriter.removeModifications(); - return result; + return fRewriter; } private void replaceParameterWithExpression(String[] expressions) { @@ -366,78 +299,75 @@ return (ASTNode)statements.get(statements.size() - 1); } - private List getReturnStatementRanges() { - List result= new ArrayList(1); - List statements= fDeclaration.getBody().statements(); - int size= statements.size(); - if (size <= 1) - return result; - result.add(createRange(statements, size - 2)); - return result; + public TextBuffer getBuffer() { + return fBuffer; } - private List getStatementRanges() { - List result= new ArrayList(1); - List statements= fDeclaration.getBody().statements(); - int size= statements.size(); - if (size == 0) - return result; - result.add(createRange(statements, size - 1)); - return result; - } - - private List getExpressionRanges() { - List result= new ArrayList(2); - List statements= fDeclaration.getBody().statements(); - ReturnStatement rs= null; - int size= statements.size(); - ASTNode node; - switch (size) { - case 0: - return result; - case 1: - node= (ASTNode)statements.get(0); - if (node.getNodeType() == ASTNode.RETURN_STATEMENT) { - rs= (ReturnStatement)node; - } else { - result.add(new Region(node.getStartPosition(), node.getLength())); - } - break; - default: { - node= (ASTNode)statements.get(size - 1); - if (node.getNodeType() == ASTNode.RETURN_STATEMENT) { - result.add(createRange(statements, size - 2)); - rs= (ReturnStatement)node; - } else { - result.add(createRange(statements, size - 1)); - } - break; + public List getSourceContent(List nodes) throws CoreException { + + int size= nodes.size(); + // the same list is used for markers and then for strings + List result= new ArrayList(size); + + TextBuffer buffer= getBuffer(); + MultiTextEdit dummy= new MultiTextEdit(); + fRewriter.rewriteNode(buffer, dummy); + TextEdit[] edits= dummy.removeChildren(); + + MultiTextEdit root= new MultiTextEdit(); + for (Iterator it= nodes.iterator(); it.hasNext();) { + ASTNode node= (ASTNode)it.next(); + RangeMarker marker= createMarker(node); + mergeEdits(marker, edits); + try { + root.addChild(marker); + } catch (MalformedTreeException e) { + // ignore } + // add marker to resulting list + result.add(marker); } - if (rs != null) { - Expression exp= rs.getExpression(); - result.add(new Region(exp.getStartPosition(), exp.getLength())); + + UndoEdit undo= performEdit(buffer, root); + + for (int i= 0; i < size; i++) { + RangeMarker marker= (RangeMarker)result.get(i); + // replace marker with node's new content + result.set(i, getContent(marker, buffer)); } + + // it is faster to undo the changes than coping the buffer over and over again + performEdit(buffer, undo); + fRewriter.removeModifications(); return result; } - private IRegion createRange(List statements, int end) { - int start= ((ASTNode)statements.get(0)).getStartPosition(); - ASTNode last= (ASTNode)statements.get(end); - int length = last.getStartPosition() - start + last.getLength(); - IRegion range= new Region(start, length); - return range; - } - - private String[] getBlocks(RangeMarker[] markers) { - String[] result= new String[markers.length]; - for (int i= 0; i < markers.length; i++) { - RangeMarker marker= markers[i]; - String content= fBuffer.getContent(marker.getOffset(), marker.getLength()); - String lines[]= Strings.convertIntoLines(content); - Strings.trimIndentation(lines, CodeFormatterUtil.getTabWidth(), false); - result[i]= Strings.concatenate(lines, fBuffer.getLineDelimiter()); + private static UndoEdit performEdit(TextBuffer buffer, TextEdit edit) throws CoreException { + TextBufferEditor editor= new TextBufferEditor(buffer); + editor.add(edit); + return editor.performEdits(null); + } + + private static UndoEdit performEdit(TextBuffer buffer, UndoEdit edit) throws CoreException { + TextBufferEditor editor= new TextBufferEditor(buffer); + editor.add(edit); + return editor.performEdits(null); + } + + private static RangeMarker createMarker(ASTNode node) { + return new RangeMarker(node.getStartPosition(), node.getLength()); + } + + private static void mergeEdits(TextEdit dest, TextEdit[] edits) { + for (int j= 0; j < edits.length; j++) { + TextEdit edit= edits[j]; + if(dest.covers(edit)) { + dest.addChild(edit); + } } - return result; } + + private static String getContent(RangeMarker marker, TextBuffer buffer) { + return InlineMethodRefactoring.getContent(marker.getOffset(), marker.getLength(), buffer); + } } Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/TargetProvider.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/TargetProvider.java,v retrieving revision 1.13 diff -u -r1.13 TargetProvider.java --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/TargetProvider.java 16 Mar 2004 08:52:41 -0000 1.13 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/TargetProvider.java 22 Mar 2004 17:40:25 -0000 @@ -45,6 +45,7 @@ import org.eclipse.jdt.internal.corext.dom.Bindings; import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine; import org.eclipse.jdt.internal.corext.refactoring.rename.RefactoringScopeFactory; +import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; import org.eclipse.ltk.core.refactoring.RefactoringStatus; abstract class TargetProvider { @@ -65,7 +66,7 @@ // constructor invocation is not an expression but a statement public abstract ASTNode[] getInvocations(BodyDeclaration declaration, IProgressMonitor pm); - public abstract RefactoringStatus checkActivation() throws JavaModelException; + public abstract RefactoringStatus checkActivation(); public abstract int getStatusSeverity(); @@ -131,7 +132,7 @@ fIterated= true; return new ASTNode[] { fInvocation }; } - public RefactoringStatus checkActivation() throws JavaModelException { + public RefactoringStatus checkActivation() { return new RefactoringStatus(); } public int getStatusSeverity() { @@ -273,7 +274,7 @@ return data.getInvocations(); } - public RefactoringStatus checkActivation() throws JavaModelException { + public RefactoringStatus checkActivation() { return new RefactoringStatus(); } @@ -305,7 +306,7 @@ } public BodyDeclaration[] getAffectedBodyDeclarations(ICompilationUnit unit, IProgressMonitor pm) { - ASTNode root= AST.parseCompilationUnit(unit, true); + ASTNode root= new RefactoringASTParser(AST.LEVEL_2_0).parse(unit, true); InvocationFinder finder= new InvocationFinder(fMethod.resolveBinding()); root.accept(finder); fCurrentBodies= finder.result; @@ -321,7 +322,7 @@ return data.getInvocations(); } - public RefactoringStatus checkActivation() throws JavaModelException { + public RefactoringStatus checkActivation() { return new RefactoringStatus(); } Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/DoWhileFlowInfo.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/DoWhileFlowInfo.java,v retrieving revision 1.3 diff -u -r1.3 DoWhileFlowInfo.java --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/DoWhileFlowInfo.java 10 Mar 2003 23:27:15 -0000 1.3 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/DoWhileFlowInfo.java 22 Mar 2004 17:40:25 -0000 @@ -17,11 +17,15 @@ public void mergeAction(FlowInfo info, FlowContext context) { if (info == null) return; - + fActionBranches= info.branches(); - + assign(info); - + + if(info.hasAnyReturn()) { + setReturningControlStatements(DO_STATEMENT); + } + if (fActionBranches && fReturnKind == VALUE_RETURN) { fReturnKind= PARTIAL_RETURN; } Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowInfo.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowInfo.java,v retrieving revision 1.11 diff -u -r1.11 FlowInfo.java --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowInfo.java 27 May 2003 14:41:50 -0000 1.11 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowInfo.java 22 Mar 2004 17:40:26 -0000 @@ -23,6 +23,15 @@ public abstract class FlowInfo { + // Flags to specify control statements containing return statements + public static final int IF_STATEMENT= 1 << 0; + public static final int FOR_STATEMENT= 1 << 1; + public static final int WHILE_STATEMENT= 1 << 2; + public static final int DO_STATEMENT= 1 << 3; + public static final int TRY_STATEMENT= 1 << 4; + public static final int SWITCH_STATEMENT= 1 << 5; + public static final int LOOP_STATEMENTS= FOR_STATEMENT | WHILE_STATEMENT | DO_STATEMENT; + // Return statement handling. protected static final int NOT_POSSIBLE= 0; protected static final int UNDEFINED= 1; @@ -37,7 +46,7 @@ public static final int READ= 1 << 1; public static final int READ_POTENTIAL= 1 << 2; public static final int WRITE= 1 << 3; - public static final int WRITE_POTENTIAL= 1 << 4; + public static final int WRITE_POTENTIAL= 1 << 4; public static final int UNKNOWN= 1 << 5; // Table to merge access modes for condition statements (e.g branch[x] || branch[y]). @@ -85,6 +94,9 @@ protected static final IVariableBinding[] EMPTY_ARRAY= new IVariableBinding[0]; protected int fReturnKind; + protected int fReturningControlStatements; + // 'true' if there is at least one particial return in the examined execution flow + protected boolean fHasAnyPartialReturn; protected int[] fAccessModes; protected HashSet fBranches; protected HashSet fExceptions; @@ -99,8 +111,15 @@ //---- General Helpers ---------------------------------------------------------- + protected void assign(FlowInfo right) { + assignExecutionFlow(right); + assignAccessMode(right); + } + protected void assignExecutionFlow(FlowInfo right) { fReturnKind= right.fReturnKind; + fReturningControlStatements= right.fReturningControlStatements; + fHasAnyPartialReturn= right.fHasAnyPartialReturn; fBranches= right.fBranches; fExceptions= right.fExceptions; } @@ -109,11 +128,6 @@ fAccessModes= right.fAccessModes; } - protected void assign(FlowInfo right) { - assignExecutionFlow(right); - assignAccessMode(right); - } - protected void mergeConditional(FlowInfo info, FlowContext context) { mergeAccessModeConditional(info, context); mergeExecutionFlowConditional(info, context); @@ -158,6 +172,22 @@ return fReturnKind == VOID_RETURN || fReturnKind == VALUE_RETURN; } + public boolean hasAnyPartialReturn() { + return fHasAnyPartialReturn; + } + + public boolean hasAnyReturn() { + return fReturnKind == VOID_RETURN || fReturnKind == VALUE_RETURN || fHasAnyPartialReturn; + } + + public boolean hasReturningControlStatement(int statementMask) { + return (fReturningControlStatements & statementMask) != 0; + } + + public void setReturningControlStatements(int statementMask) { + fReturningControlStatements= fReturningControlStatements | statementMask; + } + //---- Branches ------------------------------------------------------------------------- public boolean branches() { @@ -240,12 +270,19 @@ if (branches() && other == VALUE_RETURN) other= PARTIAL_RETURN; fReturnKind= RETURN_KIND_SEQUENTIAL_TABLE[fReturnKind][other]; + fReturningControlStatements= fReturningControlStatements | otherInfo.fReturningControlStatements; + fHasAnyPartialReturn= fHasAnyPartialReturn || + fReturnKind == PARTIAL_RETURN || other == PARTIAL_RETURN; mergeBranches(otherInfo, context); mergeExceptions(otherInfo, context); } private void mergeExecutionFlowConditional(FlowInfo otherInfo, FlowContext context) { - fReturnKind= RETURN_KIND_CONDITIONAL_TABLE[fReturnKind][otherInfo.fReturnKind]; + int other= otherInfo.fReturnKind; + fReturnKind= RETURN_KIND_CONDITIONAL_TABLE[fReturnKind][other]; + fReturningControlStatements= fReturningControlStatements | otherInfo.fReturningControlStatements; + fHasAnyPartialReturn= fHasAnyPartialReturn || + fReturnKind == PARTIAL_RETURN || other == PARTIAL_RETURN; mergeBranches(otherInfo, context); mergeExceptions(otherInfo, context); } Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/ForFlowInfo.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/ForFlowInfo.java,v retrieving revision 1.3 diff -u -r1.3 ForFlowInfo.java --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/ForFlowInfo.java 10 Mar 2003 23:27:14 -0000 1.3 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/ForFlowInfo.java 22 Mar 2004 17:40:26 -0000 @@ -36,6 +36,10 @@ if (info == null) return; + if(info.hasAnyReturn()) { + setReturningControlStatements(FOR_STATEMENT); + } + if (!context.isLoopReentranceMode()) info.mergeEmptyCondition(context); Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/IfFlowInfo.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/IfFlowInfo.java,v retrieving revision 1.3 diff -u -r1.3 IfFlowInfo.java --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/IfFlowInfo.java 10 Mar 2003 23:27:15 -0000 1.3 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/IfFlowInfo.java 22 Mar 2004 17:40:26 -0000 @@ -23,11 +23,19 @@ return; GenericConditionalFlowInfo cond= new GenericConditionalFlowInfo(); - if (thenPart != null) + if (thenPart != null) { cond.merge(thenPart, context); + if(thenPart.hasAnyReturn()) { + setReturningControlStatements(IF_STATEMENT); + } + } - if (elsePart != null) + if (elsePart != null) { cond.merge(elsePart, context); + if(elsePart.hasAnyReturn()) { + setReturningControlStatements(IF_STATEMENT); + } + } if (thenPart == null || elsePart == null) cond.mergeEmptyCondition(context); Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java,v retrieving revision 1.10 diff -u -r1.10 InOutFlowAnalyzer.java --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java 10 Mar 2003 23:27:15 -0000 1.10 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java 22 Mar 2004 17:40:26 -0000 @@ -31,6 +31,14 @@ super(context); } + public FlowInfo perform(ASTNode node) { + FlowContext context= getFlowContext(); + GenericSequentialFlowInfo result= createSequential(); + node.accept(this); + result.merge(getFlowInfo(node), context); + return result; + } + public FlowInfo perform(ASTNode[] selectedNodes) { FlowContext context= getFlowContext(); GenericSequentialFlowInfo result= createSequential(); Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java,v retrieving revision 1.11 diff -u -r1.11 InputFlowAnalyzer.java --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java 3 Oct 2003 16:14:32 -0000 1.11 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java 22 Mar 2004 17:40:26 -0000 @@ -78,7 +78,6 @@ forInfo.mergeIncrement(incrementInfo, fFlowContext); forInfo.mergeCondition(conditionInfo, fFlowContext); forInfo.mergeAction(actionInfo, fFlowContext); - forInfo.removeLabel(null); } else { // we have to merge two different cases. One if we reenter the for statement // immediatelly (that means we have to consider increments, condition and action @@ -106,7 +105,20 @@ Assert.isNotNull(fSelection); } - public FlowInfo perform(BodyDeclaration node) { + /** + * Performs the actual analysis. Accepted nodes are: all body declarations + * except type declarations and all nodes that a children of an excepted + * body declaration. + * + * @param node the node to analyze + * @return the computed flow information + */ + public FlowInfo perform(ASTNode node) { + ASTNode bodyDecl= node; + while (!(bodyDecl instanceof BodyDeclaration) && bodyDecl != null) { + bodyDecl= bodyDecl.getParent(); + } + Assert.isTrue(bodyDecl != null); Assert.isTrue(!(node instanceof TypeDeclaration)); node.accept(this); return getFlowInfo(node); Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/SwitchFlowInfo.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/SwitchFlowInfo.java,v retrieving revision 1.4 diff -u -r1.4 SwitchFlowInfo.java --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/SwitchFlowInfo.java 10 Mar 2003 23:27:15 -0000 1.4 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/SwitchFlowInfo.java 22 Mar 2004 17:40:26 -0000 @@ -29,6 +29,11 @@ fHasNullCaseInfo= true; return; } + + if(info.hasAnyReturn()) { + setReturningControlStatements(SWITCH_STATEMENT); + } + fCases.mergeConditional(info, context); } Index: core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/WhileFlowInfo.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/WhileFlowInfo.java,v retrieving revision 1.3 diff -u -r1.3 WhileFlowInfo.java --- core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/WhileFlowInfo.java 10 Mar 2003 23:27:15 -0000 1.3 +++ core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/flow/WhileFlowInfo.java 22 Mar 2004 17:40:26 -0000 @@ -22,7 +22,11 @@ public void mergeAction(FlowInfo info, FlowContext context) { if (info == null) return; - + + if(info.hasAnyReturn()) { + setReturningControlStatements(WHILE_STATEMENT); + } + if (!context.isLoopReentranceMode()) info.mergeEmptyCondition(context);