diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index f56bd1f662cb..12eeaf0a5a7e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -403,7 +403,7 @@ public class HighlightUtil { formatType(rType)); errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, assignment, message); - QuickFixAction.registerQuickFixAction(errorResult, new ChangeToAppendFix(eqOpSign, lType, rType, assignment)); + QuickFixAction.registerQuickFixAction(errorResult, new ChangeToAppendFix(eqOpSign, lType, assignment)); } return errorResult; } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeToAppendFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeToAppendFix.java index bc52ba819893..d8bb0d29aab4 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeToAppendFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeToAppendFix.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,11 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiUtil; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author Bas Leijdekkers @@ -33,13 +36,11 @@ public class ChangeToAppendFix implements IntentionAction { private final IElementType myTokenType; private final PsiType myLhsType; - private final PsiType myRhsType; private final PsiAssignmentExpression myAssignmentExpression; - public ChangeToAppendFix(IElementType eqOpSign, PsiType lType, PsiType rType, PsiAssignmentExpression assignmentExpression) { + public ChangeToAppendFix(IElementType eqOpSign, PsiType lType, PsiAssignmentExpression assignmentExpression) { myTokenType = eqOpSign; myLhsType = lType; - myRhsType = rType; myAssignmentExpression = assignmentExpression; } @@ -47,8 +48,9 @@ public class ChangeToAppendFix implements IntentionAction { @Override public String getText() { return QuickFixBundle.message("change.to.append.text", - myAssignmentExpression.getLExpression().getText(), - myAssignmentExpression.getRExpression().getText()); + buildAppendExpression(myAssignmentExpression.getRExpression(), + myLhsType.equalsToText("java.lang.Appendable"), + new StringBuilder(myAssignmentExpression.getLExpression().getText()))); } @NotNull @@ -61,9 +63,10 @@ public class ChangeToAppendFix implements IntentionAction { public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { return JavaTokenType.PLUSEQ == myTokenType && myAssignmentExpression.isValid() && - myAssignmentExpression.getManager().isInProject(myAssignmentExpression) && - myRhsType.equalsToText(CommonClassNames.JAVA_LANG_STRING) && - (myLhsType.equalsToText(CommonClassNames.JAVA_LANG_STRING_BUILDER) || myLhsType.equalsToText(CommonClassNames.JAVA_LANG_STRING_BUFFER)); + PsiManager.getInstance(project).isInProject(myAssignmentExpression) && + (myLhsType.equalsToText(CommonClassNames.JAVA_LANG_STRING_BUILDER) || + myLhsType.equalsToText(CommonClassNames.JAVA_LANG_STRING_BUFFER) || + myLhsType.equalsToText("java.lang.Appendable")); } @Override @@ -74,14 +77,78 @@ public class ChangeToAppendFix implements IntentionAction { @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { if (!CodeInsightUtilBase.prepareFileForWrite(file)) return; - final PsiExpression rExpression = myAssignmentExpression.getRExpression(); - if (rExpression == null) { + final PsiExpression rhs = myAssignmentExpression.getRExpression(); + if (rhs == null) { + return; + } + final StringBuilder appendCallText = buildAppendExpression(rhs, myLhsType.equalsToText("java.lang.Appendable"), + new StringBuilder(myAssignmentExpression.getLExpression().getText())); + if (appendCallText == null) { return; } - final String appendCallText = - myAssignmentExpression.getLExpression().getText() + ".append(" + rExpression.getText() + ')'; final PsiElementFactory factory = JavaPsiFacade.getElementFactory(myAssignmentExpression.getProject()); - final PsiExpression appendCall = factory.createExpressionFromText(appendCallText, myAssignmentExpression); + final PsiExpression appendCall = factory.createExpressionFromText(appendCallText.toString(), myAssignmentExpression); myAssignmentExpression.replace(appendCall); } + + @Nullable + private static StringBuilder buildAppendExpression(PsiExpression concatenation, boolean useStringValueOf, @NonNls StringBuilder out) + throws IncorrectOperationException { + final PsiType type = concatenation.getType(); + if (type == null) { + return null; + } + if (concatenation instanceof PsiPolyadicExpression && type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) { + PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)concatenation; + final PsiExpression[] operands = polyadicExpression.getOperands(); + boolean isConstant = true; + boolean isString = false; + final StringBuilder builder = new StringBuilder(); + for (PsiExpression operand : operands) { + if (isConstant && PsiUtil.isConstantExpression(operand)) { + if (builder.length() != 0) { + builder.append('+'); + } + final PsiType operandType = operand.getType(); + if (operandType != null && operandType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) { + isString = true; + } + builder.append(operand.getText()); + } + else { + isConstant = false; + if (builder.length() != 0) { + append(builder, useStringValueOf && !isString, out); + builder.setLength(0); + } + buildAppendExpression(operand, useStringValueOf, out); + } + } + if (builder.length() != 0) { + append(builder, false, out); + } + } + else if (concatenation instanceof PsiParenthesizedExpression) { + final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)concatenation; + final PsiExpression expression = parenthesizedExpression.getExpression(); + if (expression != null) { + return buildAppendExpression(expression, useStringValueOf, out); + } + } + else { + append(concatenation.getText(), useStringValueOf && !type.equalsToText(CommonClassNames.JAVA_LANG_STRING), out); + } + return out; + } + + private static void append(CharSequence text, boolean useStringValueOf, StringBuilder out) { + out.append(".append("); + if (useStringValueOf) { + out.append("String.valueOf(").append(text).append(')'); + } + else { + out.append(text); + } + out.append(')'); + } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeToAppend/after1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeToAppend/after1.java new file mode 100644 index 000000000000..86f4d45fe9d2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeToAppend/after1.java @@ -0,0 +1,6 @@ +// "Change to 'appendable.append(1)'" "true" +class Test { + void appendable(StringBuilder appendable) throws IOException { + appendable.append(1); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeToAppend/after2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeToAppend/after2.java new file mode 100644 index 000000000000..47e5b2b3f230 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeToAppend/after2.java @@ -0,0 +1,7 @@ +// "Change to 'builder.append(1+1).append(s).append(" ")'" "true" +class Test { + String s; + void bar(StringBuilder builder) { + builder.append(1 + 1).append(s).append(" "); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeToAppend/before1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeToAppend/before1.java new file mode 100644 index 000000000000..a10a2bf7ae44 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeToAppend/before1.java @@ -0,0 +1,6 @@ +// "Change to 'appendable.append(1)'" "true" +class Test { + void appendable(StringBuilder appendable) throws IOException { + appendable += 1; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeToAppend/before2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeToAppend/before2.java new file mode 100644 index 000000000000..0932be483d67 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeToAppend/before2.java @@ -0,0 +1,7 @@ +// "Change to 'builder.append(1+1).append(s).append(" ")'" "true" +class Test { + String s; + void bar(StringBuilder builder) { + builder += 1 + 1 + s + " "; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ChangeToAppendTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ChangeToAppendTest.java new file mode 100644 index 000000000000..c7d1a7b7a237 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ChangeToAppendTest.java @@ -0,0 +1,11 @@ +package com.intellij.codeInsight.daemon.quickFix; + +public class ChangeToAppendTest extends LightQuickFixTestCase { + + public void test() throws Exception { doAllTests(); } + + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/changeToAppend"; + } +} diff --git a/resources-en/src/messages/QuickFixBundle.properties b/resources-en/src/messages/QuickFixBundle.properties index 991d041eecce..504731f4d002 100644 --- a/resources-en/src/messages/QuickFixBundle.properties +++ b/resources-en/src/messages/QuickFixBundle.properties @@ -257,7 +257,7 @@ create.readable.property.with.field=Create getter and field for ''{0}'' create.writable.property.with.field=Create setter and field for ''{0}'' change.to.append.family=Fix StringBuilder append -change.to.append.text=Change to ''{0}.append({1})'' +change.to.append.text=Change to ''{0}'' convert.to.string.family=Fix Character Literal convert.to.string.text=Convert to String Literal