Improve quickfix to replace += operator with StringBuilder on left hand side with append() call

now works on java.lang.Appendable and splits concatenated strings into separate append calls
This commit is contained in:
Bas Leijdekkers
2012-02-22 16:57:49 +01:00
parent 5420c19cc9
commit 0a71148672
8 changed files with 120 additions and 16 deletions

View File

@@ -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;
}

View File

@@ -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(')');
}
}

View File

@@ -0,0 +1,6 @@
// "Change to 'appendable.append(1)'" "true"
class Test {
void appendable(StringBuilder appendable) throws IOException {
appendable.append(1);
}
}

View File

@@ -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(" ");
}
}

View File

@@ -0,0 +1,6 @@
// "Change to 'appendable.append(1)'" "true"
class Test {
void appendable(StringBuilder appendable) throws IOException {
appendab<caret>le += 1;
}
}

View File

@@ -0,0 +1,7 @@
// "Change to 'builder.append(1+1).append(s).append(" ")'" "true"
class Test {
String s;
void bar(StringBuilder builder) {
buil<caret>der += 1 + 1 + s + " ";
}
}

View File

@@ -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";
}
}

View File

@@ -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