Skip to content

Commit 5da0735

Browse files
authored
Text block conversion support for indexOf() (#1709)
* Enhance StringBuilder to text block cleanup to support indexOf - add new support to StringConcatToTextBlockFixCore to allow an indexOf call for StringBuilder/StringBuffer which can also work on a String once the conversion is complete (change the variable name of the indexOf call appropriately) - add new tests to CleanUpTest15 and AssistQuickFixTest15 - for #1703
1 parent 5b3e309 commit 5da0735

File tree

3 files changed

+135
-9
lines changed

3 files changed

+135
-9
lines changed

org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/StringConcatToTextBlockFixCore.java

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ public static final class StringBufferFinder extends ASTVisitor {
487487
private Map<ExpressionStatement, ChangeStringBufferToTextBlock> conversions= new HashMap<>();
488488
private static final String APPEND= "append"; //$NON-NLS-1$
489489
private static final String TO_STRING= "toString"; //$NON-NLS-1$
490+
private static final String INDEX_OF= "indexOf"; //$NON-NLS-1$
490491
private BodyDeclaration fLastBodyDecl;
491492
private final Set<String> fExcludedNames;
492493
private final Set<IBinding> fRemovedDeclarations= new HashSet<>();
@@ -503,6 +504,7 @@ private class CheckValidityVisitor extends ASTVisitor {
503504
private int lastStatementEnd;
504505
private IBinding varBinding;
505506
private List<MethodInvocation> toStringList= new ArrayList<>();
507+
private List<MethodInvocation> indexOfList= new ArrayList<>();
506508
private List<SimpleName> argList= new ArrayList<>();
507509

508510
public CheckValidityVisitor(int lastStatementEnd, IBinding varBinding) {
@@ -522,6 +524,10 @@ public List<MethodInvocation> getToStringList() {
522524
return toStringList;
523525
}
524526

527+
public List<MethodInvocation> getIndexOfList() {
528+
return indexOfList;
529+
}
530+
525531
public List<SimpleName> getArgList() {
526532
return argList;
527533
}
@@ -547,6 +553,10 @@ public boolean visit(SimpleName name) {
547553
valid= true;
548554
toStringList.add(invocation);
549555
return true;
556+
} else if (invocation.getName().getFullyQualifiedName().equals(INDEX_OF)) {
557+
valid= true;
558+
indexOfList.add(invocation);
559+
return true;
550560
} else {
551561
valid= false;
552562
}
@@ -645,6 +655,7 @@ public boolean visit(ClassInstanceCreation node) {
645655
if (exp instanceof MethodInvocation) {
646656
class MethodVisitor extends ASTVisitor {
647657
private boolean valid= false;
658+
private boolean indexOfSeen= false;
648659
public boolean isValid() {
649660
return valid;
650661
}
@@ -653,11 +664,19 @@ public boolean visit(SimpleName simpleName) {
653664
if (simpleName.getFullyQualifiedName().equals(APPEND) && simpleName.getLocationInParent() == MethodInvocation.NAME_PROPERTY) {
654665
return true;
655666
}
656-
if (simpleName.getLocationInParent() == MethodInvocation.EXPRESSION_PROPERTY && simpleName.getFullyQualifiedName().equals(originalVarName.getFullyQualifiedName())
657-
&& ((MethodInvocation)simpleName.getParent()).getName().getFullyQualifiedName().equals(APPEND)) {
658-
extractConcatenatedAppends(simpleName);
659-
valid= true;
660-
return false;
667+
if (simpleName.getLocationInParent() == MethodInvocation.EXPRESSION_PROPERTY && simpleName.getFullyQualifiedName().equals(originalVarName.getFullyQualifiedName())) {
668+
if (((MethodInvocation)simpleName.getParent()).getName().getFullyQualifiedName().equals(APPEND)) {
669+
if (!indexOfSeen) {
670+
extractConcatenatedAppends(simpleName);
671+
valid= true;
672+
} else {
673+
valid= false;
674+
}
675+
return false;
676+
} else if (((MethodInvocation)simpleName.getParent()).getName().getFullyQualifiedName().equals(INDEX_OF)) {
677+
indexOfSeen= true;
678+
return false;
679+
}
661680
}
662681
return true;
663682
}
@@ -724,12 +743,13 @@ public boolean visit(SimpleName simpleName) {
724743
List<Statement> statements= new ArrayList<>(statementList);
725744
List<StringLiteral> literals= new ArrayList<>(fLiterals);
726745
List<MethodInvocation> toStringList= new ArrayList<>(checkValidityVisitor.getToStringList());
746+
List<MethodInvocation> indexOfList= new ArrayList<>(checkValidityVisitor.getIndexOfList());
727747
List<SimpleName> argList= new ArrayList<>(checkValidityVisitor.getArgList());
728748
BodyDeclaration bodyDecl= ASTNodes.getFirstAncestorOrNull(node, BodyDeclaration.class);
729749
if (statements.get(0) instanceof VariableDeclarationStatement) {
730750
fRemovedDeclarations.add(originalVarName.resolveBinding());
731751
}
732-
ChangeStringBufferToTextBlock operation= new ChangeStringBufferToTextBlock(toStringList, argList, statements, literals,
752+
ChangeStringBufferToTextBlock operation= new ChangeStringBufferToTextBlock(toStringList, indexOfList, argList, statements, literals,
733753
fRemovedDeclarations.contains(originalVarName.resolveBinding()) ? assignmentToConvert : null, fExcludedNames, fLastBodyDecl, nonNLS);
734754
fLastBodyDecl= bodyDecl;
735755
fOperations.add(operation);
@@ -864,6 +884,7 @@ private static boolean hasNLSMarker(ASTNode node, ICompilationUnit cu) throws Ab
864884
public static class ChangeStringBufferToTextBlock extends CompilationUnitRewriteOperation {
865885

866886
private final List<MethodInvocation> fToStringList;
887+
private final List<MethodInvocation> fIndexOfList;
867888
private final List<SimpleName> fArgList;
868889
private final List<Statement> fStatements;
869890
private final List<StringLiteral> fLiterals;
@@ -873,9 +894,10 @@ public static class ChangeStringBufferToTextBlock extends CompilationUnitRewrite
873894
private final boolean fNonNLS;
874895
private ExpressionStatement fAssignmentToConvert;
875896

876-
public ChangeStringBufferToTextBlock(final List<MethodInvocation> toStringList, final List<SimpleName> argList, List<Statement> statements,
877-
List<StringLiteral> literals, ExpressionStatement assignmentToConvert, Set<String> excludedNames, BodyDeclaration lastBodyDecl, boolean nonNLS) {
897+
public ChangeStringBufferToTextBlock(final List<MethodInvocation> toStringList, final List<MethodInvocation> indexOfList, final List<SimpleName> argList,
898+
List<Statement> statements, List<StringLiteral> literals, ExpressionStatement assignmentToConvert, Set<String> excludedNames, BodyDeclaration lastBodyDecl, boolean nonNLS) {
878899
this.fToStringList= toStringList;
900+
this.fIndexOfList= indexOfList;
879901
this.fArgList= argList;
880902
this.fStatements= statements;
881903
this.fLiterals= literals;
@@ -976,6 +998,7 @@ public SourceRange computeSourceRange(final ASTNode nodeWithComment) {
976998
buf.append("\"\"\""); //$NON-NLS-1$
977999
AST ast= fStatements.get(0).getAST();
9781000
if (fToStringList.size() == 1 &&
1001+
fIndexOfList.isEmpty() &&
9791002
!fNonNLS &&
9801003
ASTNodes.getLeadingComments(fStatements.get(0)).size() == 0 &&
9811004
(fToStringList.get(0).getLocationInParent() == Assignment.RIGHT_HAND_SIDE_PROPERTY ||
@@ -1024,6 +1047,15 @@ public SourceRange computeSourceRange(final ASTNode nodeWithComment) {
10241047
SimpleName name= ast.newSimpleName(newVarName);
10251048
rewrite.replace(toStringCall, name, group);
10261049
}
1050+
for (MethodInvocation indexOfCall : fIndexOfList) {
1051+
MethodInvocation newCall= ast.newMethodInvocation();
1052+
newCall.setName(ast.newSimpleName("indexOf")); //$NON-NLS-1$
1053+
SimpleName caller= ast.newSimpleName(newVarName);
1054+
newCall.setExpression(caller);
1055+
List<Expression> arguments= indexOfCall.arguments();
1056+
newCall.arguments().add(rewrite.createCopyTarget(arguments.get(0)));
1057+
rewrite.replace(indexOfCall, newCall, group);
1058+
}
10271059
for (SimpleName arg : fArgList) {
10281060
SimpleName name= ast.newSimpleName(newVarName);
10291061
rewrite.replace(arg, name, group);

org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest15.java

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,65 @@ private void write(CharSequence c) {
717717
assertExpectedExistInProposals(proposals, new String[] { expected });
718718
}
719719

720+
@Test
721+
public void testConcatToTextBlock13() throws Exception {
722+
fJProject1= JavaProjectHelper.createJavaProject("TestProject1", "bin");
723+
fJProject1.setRawClasspath(projectSetup.getDefaultClasspath(), null);
724+
JavaProjectHelper.set15CompilerOptions(fJProject1, false);
725+
fSourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src");
726+
727+
String str= """
728+
module test {
729+
}
730+
""";
731+
IPackageFragment def= fSourceFolder.createPackageFragment("", false, null);
732+
def.createCompilationUnit("module-info.java", str, false, null);
733+
734+
IPackageFragment pack= fSourceFolder.createPackageFragment("test", false, null);
735+
StringBuilder buf= new StringBuilder();
736+
buf.append("package test;\n");
737+
buf.append("public class Cls {\n");
738+
buf.append(" public void foo() {\n");
739+
buf.append(" StringBuilder buf3= new StringBuilder();\n");
740+
buf.append(" buf3.append(\"public void foo() {\\n\");\n");
741+
buf.append(" buf3.append(\" return null;\\n\");\n");
742+
buf.append(" buf3.append(\"}\\n\");\n");
743+
buf.append(" buf3.append(\"\\n\");\n");
744+
buf.append(" // comment 1\n");
745+
buf.append(" int index = buf3.indexOf(\"null\");\n");
746+
buf.append(" String k = buf3.toString();\n");
747+
buf.append(" \n");
748+
buf.append(" }\n");
749+
buf.append("}\n");
750+
ICompilationUnit cu= pack.createCompilationUnit("Cls.java", buf.toString(), false, null);
751+
752+
int index= buf.indexOf("StringBuilder");
753+
IInvocationContext ctx= getCorrectionContext(cu, index, 4);
754+
assertNoErrors(ctx);
755+
ArrayList<IJavaCompletionProposal> proposals= collectAssists(ctx, false);
756+
757+
String expected= """
758+
package test;
759+
public class Cls {
760+
public void foo() {
761+
String str = \"""
762+
public void foo() {
763+
return null;
764+
}
765+
\t
766+
\""";
767+
// comment 1
768+
int index = str.indexOf("null");
769+
String k = str;
770+
\s
771+
}
772+
}
773+
""";
774+
775+
assertProposalExists(proposals, FixMessages.StringConcatToTextBlockFix_convert_msg);
776+
assertExpectedExistInProposals(proposals, new String[] { expected });
777+
}
778+
720779
@Test
721780
public void testNoConcatToTextBlock1() throws Exception {
722781
fJProject1= JavaProjectHelper.createJavaProject("TestProject1", "bin");
@@ -1001,8 +1060,18 @@ public void testNoConcatToTextBlock8() throws Exception {
10011060
buf.append(" String k = buf3.toString();\n");
10021061
buf.append(" \n");
10031062
buf.append(" }\n");
1063+
buf.append(" public void indexOfInside() {\n");
1064+
buf.append(" StringBuffer buf3= new StringBuffer();\n");
1065+
buf.append(" buf3.append(\"public void foo() {\\n\");\n");
1066+
buf.append(" buf3.append(\" return null;\\n\");\n");
1067+
buf.append(" buf3.append(\"}\\n\");\n");
1068+
buf.append(" int index = buf3.indexOf(\"foo\");\n");
1069+
buf.append(" buf3.append(\"\\n\");\n");
1070+
buf.append(" String k = buf3.toString();\n");
1071+
buf.append(" \n");
1072+
buf.append(" }\n");
10041073
buf.append("}\n");
1005-
ICompilationUnit cu= pack.createCompilationUnit("Cls.java", buf.toString(), false, null);
1074+
ICompilationUnit cu= pack.createCompilationUnit("Cls.java", buf.toString(), false, null);
10061075

10071076
int index= buf.indexOf("buf3");
10081077
IInvocationContext ctx= getCorrectionContext(cu, index, 6);

org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest15.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,13 @@ public void foo() {
401401
buf10.append(" * foo\\n");
402402
buf10.append(" */");
403403
write(buf10);
404+
StringBuilder buf11 = new StringBuilder();
405+
buf11.append("abcd\\n");
406+
buf11.append("efgh\\n");
407+
buf11.append("ijkl\\n");
408+
buf11.append("mnop\\n");
409+
int index = buf11.indexOf("fg");
410+
System.out.println(buf11.toString());
404411
}
405412
private void write(CharSequence c) {
406413
System.out.println(c);
@@ -522,6 +529,14 @@ public class C {
522529
*/\\
523530
\""";
524531
write(str8);
532+
String str9 = \"""
533+
abcd
534+
efgh
535+
ijkl
536+
mnop
537+
\""";
538+
int index = str9.indexOf("fg");
539+
System.out.println(str9);
525540
}
526541
private void write(CharSequence c) {
527542
System.out.println(c);
@@ -792,6 +807,16 @@ public void testInconsistentNLS() {
792807
buf.append(3);
793808
String x = buf.toString();
794809
}
810+
811+
public void testIndexOfInBetween() {
812+
StringBuffer buf = new StringBuffer();
813+
buf.append("abcdef\\n");
814+
buf.append("123456\\n");
815+
buf.append("ghijkl\\n");
816+
int index = buf.indexOf("123");
817+
buf.append(3);
818+
String x = buf.toString();
819+
}
795820
}
796821
""";
797822
ICompilationUnit cu1= pack1.createCompilationUnit("E.java", sample, false, null);

0 commit comments

Comments
 (0)