extract method object: wrap if/loop bodies in {} when needed (IDEA-92156)

This commit is contained in:
Anna Kozlova
2012-10-13 18:49:53 +02:00
parent a2b5bd3484
commit 58d7db1d6f
7 changed files with 167 additions and 98 deletions

View File

@@ -269,12 +269,39 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor {
final List<PsiLocalVariable> vars = new ArrayList<PsiLocalVariable>();
final Map<PsiElement, PsiElement> replacementMap = new LinkedHashMap<PsiElement, PsiElement>();
final List<PsiReturnStatement> returnStatements = new ArrayList<PsiReturnStatement>();
body.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReturnStatement(PsiReturnStatement statement) {
returnStatements.add(statement);
}
});
if (myExtractProcessor.generatesConditionalExit()) {
for (int i = 0; i < returnStatements.size() - 1; i++) {
final PsiReturnStatement condition = returnStatements.get(i);
final PsiElement container = condition.getParent();
final PsiStatement resultStmt = myElementFactory.createStatementFromText("myResult = true;", container);
if (!RefactoringUtil.isLoopOrIf(container)) {
container.addBefore(resultStmt, condition);
} else {
RefactoringUtil.putStatementInLoopBody(resultStmt, container, condition);
}
}
LOG.assertTrue(!returnStatements.isEmpty());
final PsiReturnStatement returnStatement = returnStatements.get(returnStatements.size() - 1);
final PsiElement container = returnStatement.getParent();
final PsiStatement resultStmt = myElementFactory.createStatementFromText("myResult = false;", container);
if (!RefactoringUtil.isLoopOrIf(container)) {
container.addBefore(resultStmt, returnStatement);
} else {
RefactoringUtil.putStatementInLoopBody(resultStmt, container, returnStatement);
}
}
body.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReturnStatement(final PsiReturnStatement statement) {
super.visitReturnStatement(statement);
try {
returnStatements.add(statement);
replacementMap.put(statement, myElementFactory.createStatementFromText("return this;", statement));
}
catch (IncorrectOperationException e) {
@@ -324,17 +351,6 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor {
}
});
if (myExtractProcessor.generatesConditionalExit()) {
for (int i = 0; i < returnStatements.size() - 1; i++) {
final PsiReturnStatement condition = returnStatements.get(i);
condition.getParent().addBefore(myElementFactory.createStatementFromText("myResult = true;", condition), condition);
}
LOG.assertTrue(!returnStatements.isEmpty());
final PsiReturnStatement returnStatement = returnStatements.get(returnStatements.size() - 1);
returnStatement.getParent().addBefore(myElementFactory.createStatementFromText("myResult = false;", returnStatement), returnStatement);
}
for (PsiLocalVariable var : vars) {
final String fieldName = var2FieldNames.get(var.getName());
for (PsiReference reference : ReferencesSearch.search(var)) {

View File

@@ -760,7 +760,7 @@ public abstract class BaseExpressionToFieldHandler extends IntroduceHandlerBase
anchorElementHere = myAnchorElementIfOne;
}
assignStatement = createAssignment(myField, initializer, anchorElementHere, myParentClass);
if (!IntroduceVariableBase.isLoopOrIf(anchorElementHere.getParent())) {
if (!RefactoringUtil.isLoopOrIf(anchorElementHere.getParent())) {
anchorElementHere.getParent().addBefore(assignStatement, getNormalizedAnchor(anchorElementHere));
}
}
@@ -821,8 +821,8 @@ public abstract class BaseExpressionToFieldHandler extends IntroduceHandlerBase
}
}
if (anchorElementHere != null && IntroduceVariableBase.isLoopOrIf(anchorElementHere.getParent())) {
IntroduceVariableBase.putStatementInLoopBody(assignStatement, anchorElementHere.getParent(), anchorElementHere);
if (anchorElementHere != null && RefactoringUtil.isLoopOrIf(anchorElementHere.getParent())) {
RefactoringUtil.putStatementInLoopBody(assignStatement, anchorElementHere.getParent(), anchorElementHere);
}

View File

@@ -525,7 +525,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
final PsiElement tempContainer = anchorStatement.getParent();
if (!(tempContainer instanceof PsiCodeBlock) && !isLoopOrIf(tempContainer) && (tempContainer.getParent() instanceof PsiLambdaExpression)) {
if (!(tempContainer instanceof PsiCodeBlock) && !RefactoringUtil.isLoopOrIf(tempContainer) && (tempContainer.getParent() instanceof PsiLambdaExpression)) {
String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME);
showErrorMessage(project, editor, message);
return false;
@@ -714,7 +714,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
final PsiElement container = tempContainer;
PsiElement child = anchorStatement;
if (!isLoopOrIf(container)) {
if (!RefactoringUtil.isLoopOrIf(container)) {
child = locateAnchor(child);
if (isFinalVariableOnLHS(expr)) {
child = child.getNextSibling();
@@ -724,7 +724,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
boolean tempDeleteSelf = false;
final boolean replaceSelf = settings.isReplaceLValues() || !RefactoringUtil.isAssignmentLHS(expr);
if (!isLoopOrIf(container)) {
if (!RefactoringUtil.isLoopOrIf(container)) {
if (expr.getParent() instanceof PsiExpressionStatement && anchor.equals(anchorStatement)) {
PsiStatement statement = (PsiStatement) expr.getParent();
PsiElement parent = statement.getParent();
@@ -755,7 +755,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
public void run() {
try {
PsiStatement statement = null;
final boolean isInsideLoop = isLoopOrIf(container);
final boolean isInsideLoop = RefactoringUtil.isLoopOrIf(container);
if (!isInsideLoop && deleteSelf) {
statement = (PsiStatement) expr.getParent();
}
@@ -823,7 +823,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
}
}
declaration = (PsiDeclarationStatement) putStatementInLoopBody(declaration, container, finalAnchorStatement);
declaration = (PsiDeclarationStatement) RefactoringUtil.putStatementInLoopBody(declaration, container, finalAnchorStatement);
declaration = (PsiDeclarationStatement)JavaCodeStyleManager.getInstance(project).shortenClassReferences(declaration);
PsiVariable var = (PsiVariable) declaration.getDeclaredElements()[0];
PsiUtil.setModifierProperty(var, PsiModifier.FINAL, settings.isDeclareFinal());
@@ -912,60 +912,6 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
return JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(text, parent);
}
public static PsiStatement putStatementInLoopBody(PsiStatement declaration, PsiElement container, PsiElement finalAnchorStatement)
throws IncorrectOperationException {
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(container.getProject()).getElementFactory();
if(isLoopOrIf(container)) {
PsiStatement loopBody = getLoopBody(container, finalAnchorStatement);
PsiStatement loopBodyCopy = loopBody != null ? (PsiStatement) loopBody.copy() : null;
PsiBlockStatement blockStatement = (PsiBlockStatement)elementFactory
.createStatementFromText("{}", null);
blockStatement = (PsiBlockStatement) CodeStyleManager.getInstance(container.getProject()).reformat(blockStatement);
final PsiElement prevSibling = loopBody.getPrevSibling();
if(prevSibling instanceof PsiWhiteSpace) {
final PsiElement pprev = prevSibling.getPrevSibling();
if (!(pprev instanceof PsiComment) || !((PsiComment)pprev).getTokenType().equals(JavaTokenType.END_OF_LINE_COMMENT)) {
prevSibling.delete();
}
}
blockStatement = (PsiBlockStatement) loopBody.replace(blockStatement);
final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
declaration = (PsiStatement) codeBlock.add(declaration);
JavaCodeStyleManager.getInstance(declaration.getProject()).shortenClassReferences(declaration);
if (loopBodyCopy != null) codeBlock.add(loopBodyCopy);
} else if (container instanceof PsiLambdaExpression) {
final PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)container;
final PsiElement lambdaExpressionBody = lambdaExpression.getBody();
LOG.assertTrue(lambdaExpressionBody != null);
final PsiLambdaExpression expressionFromText = (PsiLambdaExpression)elementFactory
.createExpressionFromText(lambdaExpression.getParameterList().getText() + " -> {}", lambdaExpression);
PsiCodeBlock newBody = (PsiCodeBlock)expressionFromText.getBody();
LOG.assertTrue(newBody != null);
newBody.add(declaration);
final PsiStatement lastBodyStatement;
if (LambdaUtil.getFunctionalInterfaceReturnType(lambdaExpression) == PsiType.VOID) {
lastBodyStatement = elementFactory.createStatementFromText("a;", lambdaExpression);
((PsiExpressionStatement)lastBodyStatement).getExpression().replace(lambdaExpressionBody);
}
else {
lastBodyStatement = elementFactory.createStatementFromText("return a;", lambdaExpression);
final PsiExpression returnValue = ((PsiReturnStatement)lastBodyStatement).getReturnValue();
LOG.assertTrue(returnValue != null);
returnValue.replace(lambdaExpressionBody);
}
newBody.add(lastBodyStatement);
final PsiLambdaExpression copy = (PsiLambdaExpression)lambdaExpression.replace(expressionFromText);
newBody = (PsiCodeBlock)copy.getBody();
LOG.assertTrue(newBody != null);
declaration = newBody.getStatements()[0];
declaration = (PsiStatement)JavaCodeStyleManager.getInstance(declaration.getProject()).shortenClassReferences(declaration);
}
return declaration;
}
private boolean parentStatementNotFound(final Project project, Editor editor) {
String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME);
showErrorMessage(project, editor, message);
@@ -1002,30 +948,6 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
protected abstract void showErrorMessage(Project project, Editor editor, String message);
@Nullable
private static PsiStatement getLoopBody(PsiElement container, PsiElement anchorStatement) {
if(container instanceof PsiLoopStatement) {
return ((PsiLoopStatement) container).getBody();
}
else if (container instanceof PsiIfStatement) {
final PsiStatement thenBranch = ((PsiIfStatement)container).getThenBranch();
if (thenBranch != null && PsiTreeUtil.isAncestor(thenBranch, anchorStatement, false)) {
return thenBranch;
}
final PsiStatement elseBranch = ((PsiIfStatement)container).getElseBranch();
if (elseBranch != null && PsiTreeUtil.isAncestor(elseBranch, anchorStatement, false)) {
return elseBranch;
}
LOG.assertTrue(false);
}
LOG.assertTrue(false);
return null;
}
public static boolean isLoopOrIf(PsiElement element) {
return element instanceof PsiLoopStatement || element instanceof PsiIfStatement;
}
protected boolean reportConflicts(MultiMap<PsiElement,String> conflicts, Project project, IntroduceVariableSettings settings){
return false;

View File

@@ -965,6 +965,84 @@ public class RefactoringUtil {
return false;
}
public static PsiStatement putStatementInLoopBody(PsiStatement declaration, PsiElement container, PsiElement finalAnchorStatement)
throws IncorrectOperationException {
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(container.getProject()).getElementFactory();
if(isLoopOrIf(container)) {
PsiStatement loopBody = getLoopBody(container, finalAnchorStatement);
PsiStatement loopBodyCopy = loopBody != null ? (PsiStatement) loopBody.copy() : null;
PsiBlockStatement blockStatement = (PsiBlockStatement)elementFactory
.createStatementFromText("{}", null);
blockStatement = (PsiBlockStatement) CodeStyleManager.getInstance(container.getProject()).reformat(blockStatement);
final PsiElement prevSibling = loopBody.getPrevSibling();
if(prevSibling instanceof PsiWhiteSpace) {
final PsiElement pprev = prevSibling.getPrevSibling();
if (!(pprev instanceof PsiComment) || !((PsiComment)pprev).getTokenType().equals(JavaTokenType.END_OF_LINE_COMMENT)) {
prevSibling.delete();
}
}
blockStatement = (PsiBlockStatement) loopBody.replace(blockStatement);
final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
declaration = (PsiStatement) codeBlock.add(declaration);
JavaCodeStyleManager.getInstance(declaration.getProject()).shortenClassReferences(declaration);
if (loopBodyCopy != null) codeBlock.add(loopBodyCopy);
} else if (container instanceof PsiLambdaExpression) {
final PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)container;
final PsiElement lambdaExpressionBody = lambdaExpression.getBody();
LOG.assertTrue(lambdaExpressionBody != null);
final PsiLambdaExpression expressionFromText = (PsiLambdaExpression)elementFactory
.createExpressionFromText(lambdaExpression.getParameterList().getText() + " -> {}", lambdaExpression);
PsiCodeBlock newBody = (PsiCodeBlock)expressionFromText.getBody();
LOG.assertTrue(newBody != null);
newBody.add(declaration);
final PsiStatement lastBodyStatement;
if (LambdaUtil.getFunctionalInterfaceReturnType(lambdaExpression) == PsiType.VOID) {
lastBodyStatement = elementFactory.createStatementFromText("a;", lambdaExpression);
((PsiExpressionStatement)lastBodyStatement).getExpression().replace(lambdaExpressionBody);
}
else {
lastBodyStatement = elementFactory.createStatementFromText("return a;", lambdaExpression);
final PsiExpression returnValue = ((PsiReturnStatement)lastBodyStatement).getReturnValue();
LOG.assertTrue(returnValue != null);
returnValue.replace(lambdaExpressionBody);
}
newBody.add(lastBodyStatement);
final PsiLambdaExpression copy = (PsiLambdaExpression)lambdaExpression.replace(expressionFromText);
newBody = (PsiCodeBlock)copy.getBody();
LOG.assertTrue(newBody != null);
declaration = newBody.getStatements()[0];
declaration = (PsiStatement)JavaCodeStyleManager.getInstance(declaration.getProject()).shortenClassReferences(declaration);
}
return declaration;
}
@Nullable
private static PsiStatement getLoopBody(PsiElement container, PsiElement anchorStatement) {
if(container instanceof PsiLoopStatement) {
return ((PsiLoopStatement) container).getBody();
}
else if (container instanceof PsiIfStatement) {
final PsiStatement thenBranch = ((PsiIfStatement)container).getThenBranch();
if (thenBranch != null && PsiTreeUtil.isAncestor(thenBranch, anchorStatement, false)) {
return thenBranch;
}
final PsiStatement elseBranch = ((PsiIfStatement)container).getElseBranch();
if (elseBranch != null && PsiTreeUtil.isAncestor(elseBranch, anchorStatement, false)) {
return elseBranch;
}
LOG.assertTrue(false);
}
LOG.assertTrue(false);
return null;
}
public static boolean isLoopOrIf(PsiElement element) {
return element instanceof PsiLoopStatement || element instanceof PsiIfStatement;
}
public interface ImplicitConstructorUsageVisitor {
void visitConstructor(PsiMethod constructor, PsiMethod baseConstructor);

View File

@@ -0,0 +1,10 @@
class Test {
void foo(String[] args){
for (String aArg : args) {
<selection>boolean a = aArg == null;
if (aArg == null) continue;</selection>
System.out.println(aArg + a);
}
}
}

View File

@@ -0,0 +1,39 @@
class Test {
void foo(String[] args){
for (String aArg : args) {
Inner inner = new Inner(aArg).invoke();
if (inner.is()) continue;
boolean a = inner.isA();
System.out.println(aArg + a);
}
}
private class Inner {
private boolean myResult;
private String aArg;
private boolean a;
public Inner(String aArg) {
this.aArg = aArg;
}
boolean is() {
return myResult;
}
public boolean isA() {
return a;
}
public Inner invoke() {
a = aArg == null;
if (aArg == null) {
myResult = true;
return this;
}
myResult = false;
return this;
}
}
}

View File

@@ -110,4 +110,8 @@ public class ExtractMethodObjectWithMultipleExitPointsTest extends LightRefactor
public void testExtractFromIfStatementInsideAnonymous() throws Exception {
doTest();
}
public void testConditionalExitWithoutCodeBlock() throws Exception {
doTest();
}
}