diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/CleanUpRegistry.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/CleanUpRegistry.java index a7061693b6..5c917933ea 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/CleanUpRegistry.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/CleanUpRegistry.java @@ -19,8 +19,11 @@ import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IBuffer; +import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.manipulation.CleanUpContextCore; import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.lsp4j.TextDocumentIdentifier; @@ -42,6 +45,10 @@ public CleanUpRegistry() { cleanUpsList.add(new AddDeprecatedAnnotationCleanUp()); cleanUpsList.add(new StringConcatToTextBlockCleanUp()); cleanUpsList.add(new InvertEqualsCleanUp()); + cleanUpsList.add(new VariableDeclarationFixCleanup()); + cleanUpsList.add(new SwitchExpressionCleanup()); + cleanUpsList.add(new InstanceofPatternMatch()); + cleanUpsList.add(new LambdaExpressionCleanup()); // Store in a Map so that they can be accessed by ID quickly cleanUps = new HashMap<>(); @@ -94,10 +101,31 @@ public List getEditsForAllActiveCleanUps(TextDocumentIdentifier textDo // build the context after setting the compiler options so that the built AST has all the required markers CleanUpContextCore context = CleanUpUtils.getCleanUpContext(textDocumentId, opts, monitor); - List textEdits = new ArrayList<>(); - for (ISimpleCleanUp cleanUp : cleanUpsToRun) { - textEdits.addAll(CleanUpUtils.getTextEditFromCleanUp(cleanUp, context, monitor)); + ICompilationUnit cu = context.getCompilationUnit(); + + try { + ICompilationUnit wc = cu.getWorkingCopy(monitor); + for (ISimpleCleanUp cleanUp : cleanUpsToRun) { + org.eclipse.text.edits.TextEdit jdtEdit = CleanUpUtils.getTextEditFromCleanUp(cleanUp, context, monitor); + if (jdtEdit != null) { + wc.applyTextEdit(jdtEdit, monitor); + context = CleanUpUtils.getCleanUpContext(wc, opts, monitor); + } + } + // https://microsoft.github.io/language-server-protocol/specifications/specification-3-16/#textEditArray + // Cleanups may have overlapping text edits but LSP does not support this + // Generate text edit as the entire document + IBuffer wcBuff = wc.getBuffer(); + IBuffer cuBuff = cu.getBuffer(); + String newText = wcBuff.getContents(); + if (!newText.equals(cuBuff.getContents())) { + TextEdit te = new TextEdit(JDTUtils.toRange(cu, 0, cuBuff.getLength()), newText); + textEdits.add(te); + } + + } catch (JavaModelException e) { + // continue } return textEdits; diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/CleanUpUtils.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/CleanUpUtils.java index 65ca5b187a..ac6f6e7ef0 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/CleanUpUtils.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/CleanUpUtils.java @@ -12,8 +12,6 @@ *******************************************************************************/ package org.eclipse.jdt.ls.core.internal.cleanup; -import java.util.Collections; -import java.util.List; import java.util.Map; import org.eclipse.core.runtime.CoreException; @@ -27,9 +25,8 @@ import org.eclipse.jdt.internal.corext.dom.IASTSharedValues; import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; -import org.eclipse.jdt.ls.core.internal.TextEditConverter; import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.TextEdit; +import org.eclipse.text.edits.TextEdit; /** * Functions for working with JDT ICleanUpCore and ISimpleCleanUp. @@ -49,6 +46,10 @@ public class CleanUpUtils { */ public static CleanUpContextCore getCleanUpContext(TextDocumentIdentifier textDocumentId, Map compilerOpts, IProgressMonitor monitor) { ICompilationUnit unit = JDTUtils.resolveCompilationUnit(textDocumentId.getUri()); + return getCleanUpContext(unit, compilerOpts, monitor); + } + + public static CleanUpContextCore getCleanUpContext(ICompilationUnit unit, Map compilerOpts, IProgressMonitor monitor) { CompilationUnit ast = createASTWithOpts(unit, compilerOpts, monitor); return new CleanUpContextCore(unit, ast); } @@ -64,25 +65,20 @@ public static CleanUpContextCore getCleanUpContext(TextDocumentIdentifier textDo * the progress monitor * @return a non-null list of text edits for the given clean up */ - public static List getTextEditFromCleanUp(ISimpleCleanUp cleanUp, CleanUpContextCore context, IProgressMonitor monitor) { + public static TextEdit getTextEditFromCleanUp(ISimpleCleanUp cleanUp, CleanUpContextCore context, IProgressMonitor monitor) { try { ICleanUpFixCore fix = cleanUp != null ? cleanUp.createFix(context) : null; if (fix == null) { - return Collections.emptyList(); + return null; } CompilationUnitChange cleanUpChange = fix.createChange(monitor); - org.eclipse.text.edits.TextEdit jdtEdit = cleanUpChange.getEdit(); - if (jdtEdit == null) { - return Collections.emptyList(); - } - TextEditConverter converter = new TextEditConverter(context.getCompilationUnit(), jdtEdit); - List lspEdit = converter.convert(); - return lspEdit != null ? lspEdit : Collections.emptyList(); + TextEdit jdtEdit = cleanUpChange.getEdit(); + return jdtEdit; } catch (CoreException e) { JavaLanguageServerPlugin.logError(String.format("Failed to create text edit for clean up %s", cleanUp.getIdentifier())); } - return Collections.emptyList(); + return null; } private static CompilationUnit createASTWithOpts(ICompilationUnit cu, Map opts, IProgressMonitor monitor) { diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/InstanceofPatternMatch.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/InstanceofPatternMatch.java new file mode 100644 index 0000000000..ae794469be --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/InstanceofPatternMatch.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.cleanup; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.manipulation.CleanUpContextCore; +import org.eclipse.jdt.core.manipulation.ICleanUpFixCore; +import org.eclipse.jdt.internal.corext.fix.PatternMatchingForInstanceofFixCore; + +/** + * Represents a cleanup that uses pattern matching for 'instanceof' cast checks + */ +public class InstanceofPatternMatch implements ISimpleCleanUp { + + /* (non-Javadoc) + * @see org.eclipse.jdt.ls.core.internal.cleanup.ISimpleCleanUp#getIdentifier() + */ + @Override + public String getIdentifier() { + return "instanceofPatternMatch"; + } + + /* (non-Javadoc) + * @see org.eclipse.jdt.ls.core.internal.cleanup.ISimpleCleanUp#createFix(org.eclipse.jdt.core.manipulation.CleanUpContextCore) + */ + @Override + public ICleanUpFixCore createFix(CleanUpContextCore context) throws CoreException { + CompilationUnit unit = context.getAST(); + if (unit == null) { + return null; + } + return PatternMatchingForInstanceofFixCore.createCleanUp(unit); + } + + /* (non-Javadoc) + * @see org.eclipse.jdt.ls.core.internal.cleanup.ISimpleCleanUp#getRequiredCompilerMarkers() + */ + @Override + public List getRequiredCompilerMarkers() { + return Collections.emptyList(); + } + +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/LambdaExpressionCleanup.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/LambdaExpressionCleanup.java new file mode 100644 index 0000000000..02fe3629ff --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/LambdaExpressionCleanup.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.cleanup; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.manipulation.CleanUpContextCore; +import org.eclipse.jdt.core.manipulation.ICleanUpFixCore; +import org.eclipse.jdt.internal.corext.fix.LambdaExpressionsFixCore; + +/** + * Represents a cleanup that converts an anonymous class creation to a lambda expression + */ +public class LambdaExpressionCleanup implements ISimpleCleanUp { + + /* (non-Javadoc) + * @see org.eclipse.jdt.ls.core.internal.cleanup.ISimpleCleanUp#getIdentifier() + */ + @Override + public String getIdentifier() { + return "lambdaExpression"; + } + + /* (non-Javadoc) + * @see org.eclipse.jdt.ls.core.internal.cleanup.ISimpleCleanUp#createFix(org.eclipse.jdt.core.manipulation.CleanUpContextCore) + */ + @Override + public ICleanUpFixCore createFix(CleanUpContextCore context) throws CoreException { + CompilationUnit unit = context.getAST(); + if (unit == null) { + return null; + } + return LambdaExpressionsFixCore.createCleanUp(unit, true, false); + } + + /* (non-Javadoc) + * @see org.eclipse.jdt.ls.core.internal.cleanup.ISimpleCleanUp#getRequiredCompilerMarkers() + */ + @Override + public List getRequiredCompilerMarkers() { + return Collections.emptyList(); + } + +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/SwitchExpressionCleanup.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/SwitchExpressionCleanup.java new file mode 100644 index 0000000000..4db4c6db44 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/SwitchExpressionCleanup.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.cleanup; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.manipulation.CleanUpContextCore; +import org.eclipse.jdt.core.manipulation.ICleanUpFixCore; +import org.eclipse.jdt.internal.corext.fix.SwitchExpressionsFixCore; + +/** + * Represents a cleanup that converts a switch statement to a switch expression + */ +public class SwitchExpressionCleanup implements ISimpleCleanUp { + + /* (non-Javadoc) + * @see org.eclipse.jdt.ls.core.internal.cleanup.ISimpleCleanUp#getIdentifier() + */ + @Override + public String getIdentifier() { + return "switchExpression"; + } + + /* (non-Javadoc) + * @see org.eclipse.jdt.ls.core.internal.cleanup.ISimpleCleanUp#createFix(org.eclipse.jdt.core.manipulation.CleanUpContextCore) + */ + @Override + public ICleanUpFixCore createFix(CleanUpContextCore context) throws CoreException { + CompilationUnit unit = context.getAST(); + if (unit == null) { + return null; + } + return SwitchExpressionsFixCore.createCleanUp(unit); + } + + /* (non-Javadoc) + * @see org.eclipse.jdt.ls.core.internal.cleanup.ISimpleCleanUp#getRequiredCompilerMarkers() + */ + @Override + public List getRequiredCompilerMarkers() { + return Collections.emptyList(); + } + +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/VariableDeclarationFixCleanup.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/VariableDeclarationFixCleanup.java new file mode 100644 index 0000000000..bdd072da4a --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/cleanup/VariableDeclarationFixCleanup.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2022 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.cleanup; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.manipulation.CleanUpContextCore; +import org.eclipse.jdt.core.manipulation.ICleanUpFixCore; +import org.eclipse.jdt.internal.corext.fix.VariableDeclarationFixCore; + +/** + * Represents a cleanup that adds the 'final' modifier where possible + */ +public class VariableDeclarationFixCleanup implements ISimpleCleanUp { + + /* (non-Javadoc) + * @see org.eclipse.jdt.ls.core.internal.cleanup.ISimpleCleanUp#getIdentifier() + */ + @Override + public String getIdentifier() { + return "addFinalModifier"; + } + + /* (non-Javadoc) + * @see org.eclipse.jdt.ls.core.internal.cleanup.ISimpleCleanUp#createFix(org.eclipse.jdt.core.manipulation.CleanUpContextCore) + */ + @Override + public ICleanUpFixCore createFix(CleanUpContextCore context) throws CoreException { + CompilationUnit unit = context.getAST(); + if (unit == null) { + return null; + } + return VariableDeclarationFixCore.createCleanUp(unit, true, true, true); + } + + /* (non-Javadoc) + * @see org.eclipse.jdt.ls.core.internal.cleanup.ISimpleCleanUp#getRequiredCompilerMarkers() + */ + @Override + public List getRequiredCompilerMarkers() { + return Collections.emptyList(); + } + +} diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/cleanup/CleanUpsTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/cleanup/CleanUpsTest.java index 60237a3a3e..a162807351 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/cleanup/CleanUpsTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/cleanup/CleanUpsTest.java @@ -29,11 +29,10 @@ import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; +import org.eclipse.jdt.ls.core.internal.TextEditUtil; import org.eclipse.jdt.ls.core.internal.WorkspaceHelper; import org.eclipse.jdt.ls.core.internal.correction.TestOptions; import org.eclipse.jdt.ls.core.internal.managers.AbstractMavenBasedTest; -import org.eclipse.lsp4j.Position; -import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.TextEdit; import org.junit.After; @@ -57,9 +56,12 @@ public void setup() throws Exception { importProjects("maven/quickstart"); project = WorkspaceHelper.getProject("quickstart"); javaProject = JavaCore.create(project); + Hashtable options = TestOptions.getDefaultOptions(); + JavaCore.setComplianceOptions(JavaCore.VERSION_19, options); options.put(DefaultCodeFormatterConstants.FORMATTER_NUMBER_OF_EMPTY_LINES_TO_PRESERVE, String.valueOf(99)); javaProject.setOptions(options); + fSourceFolder = javaProject.getPackageFragmentRoot(javaProject.getProject().getFolder("src/main/java")); File src = fSourceFolder.getResource().getLocation().toFile(); src.mkdirs(); @@ -108,9 +110,19 @@ public void testOverrideCleanUp() throws Exception { ICompilationUnit unit = pack1.createCompilationUnit("A.java", contents, false, monitor); String uri = unit.getUnderlyingResource().getLocationURI().toString(); + String expected = "package test1;\n" // + + "public class A implements Runnable {\n" // + + " @Override\n" // + + " public void run() {} \n" // + + " /**\n" // + + " * @deprecated\n" // + + " */\n" // + + " public void destroy() {} \n" // + + "}\n" // + + ""; List textEdits = registry.getEditsForAllActiveCleanUps(new TextDocumentIdentifier(uri), Arrays.asList("addOverride"), monitor); - assertEquals(1, textEdits.size()); - assertEquals(te("@Override\n ", r(2, 4, 2, 4)), textEdits.get(0)); + String actual = TextEditUtil.apply(unit, textEdits); + assertEquals(expected, actual); } @Test @@ -128,9 +140,19 @@ public void testDeprecatedCleanUp() throws Exception { ICompilationUnit unit = pack1.createCompilationUnit("A.java", contents, false, monitor); String uri = unit.getUnderlyingResource().getLocationURI().toString(); + String expected = "package test1;\n" // + + "public class A implements Runnable {\n" // + + " public void run() {} \n" // + + " /**\n" // + + " * @deprecated\n" // + + " */\n" // + + " @Deprecated\n" // + + " public void destroy() {} \n" // + + "}\n" // + + ""; List textEdits = registry.getEditsForAllActiveCleanUps(new TextDocumentIdentifier(uri), Arrays.asList("addDeprecated"), monitor); - assertEquals(1, textEdits.size()); - assertEquals(te("@Deprecated\n ", r(6, 4, 6, 4)), textEdits.get(0)); + String actual = TextEditUtil.apply(unit, textEdits); + assertEquals(expected, actual); } @Test @@ -146,9 +168,17 @@ public void testUseThisCleanUp() throws Exception { ICompilationUnit unit = pack1.createCompilationUnit("A.java", contents, false, monitor); String uri = unit.getUnderlyingResource().getLocationURI().toString(); + String expected = "package test1;\n" // + + "public class A {\n" // + + " private int value;\n" // + + " public int getValue() {\n" // + + " return this.value;\n" // + + " }\n" // + + "}\n" // + + ""; List textEdits = registry.getEditsForAllActiveCleanUps(new TextDocumentIdentifier(uri), Arrays.asList("qualifyMembers"), monitor); - assertEquals(1, textEdits.size()); - assertEquals(te("this.value", r(4, 15, 4, 20)), textEdits.get(0)); + String actual = TextEditUtil.apply(unit, textEdits); + assertEquals(expected, actual); } @Test @@ -164,9 +194,17 @@ public void testUseClassNameCleanUp1() throws Exception { ICompilationUnit unit = pack1.createCompilationUnit("A.java", contents, false, monitor); String uri = unit.getUnderlyingResource().getLocationURI().toString(); + String expected = "package test1;\n" // + + "public class A {\n" // + + " private static final int VALUE = 10;\n" // + + " public int getValue() {\n" // + + " return A.VALUE;\n" // + + " }\n" // + + "}\n" // + + ""; List textEdits = registry.getEditsForAllActiveCleanUps(new TextDocumentIdentifier(uri), Arrays.asList("qualifyStaticMembers"), monitor); - assertEquals(1, textEdits.size()); - assertEquals(te("A.VALUE", r(4, 15, 4, 20)), textEdits.get(0)); + String actual = TextEditUtil.apply(unit, textEdits); + assertEquals(expected, actual); } @Test @@ -184,9 +222,19 @@ public void testUseClassNameCleanUp2() throws Exception { ICompilationUnit unit = pack1.createCompilationUnit("A.java", contents, false, monitor); String uri = unit.getUnderlyingResource().getLocationURI().toString(); + String expected = "" + // + "package test1;\n" + // + "import static java.lang.System.out;\n" + // + "public class A {\n" + // + " private static final int VALUE = 10;\n" + // + " public int getValue() {\n" + // + " System.out.println(\"moo\");\n" + // + " return A.VALUE;\n" + // + " }\n" + // + "}\n"; List textEdits = registry.getEditsForAllActiveCleanUps(new TextDocumentIdentifier(uri), Arrays.asList("qualifyStaticMembers"), monitor); - assertEquals(1, textEdits.size()); - assertEquals(te("System.out", r(5, 8, 5, 11)), textEdits.get(0)); + String actual = TextEditUtil.apply(unit, textEdits); + assertEquals(expected, actual); } @Test @@ -233,25 +281,244 @@ public void testInvertEqualsCleanUp() throws Exception { ICompilationUnit unit = pack1.createCompilationUnit("A.java", contents, false, monitor); String uri = unit.getUnderlyingResource().getLocationURI().toString(); + String expected = "package test1;\n" // + + "public class A {\n" // + + " String message;\n" // + + " boolean result1 = \"text\".equals(message);\n" // + + " boolean result2 = \"text\".equalsIgnoreCase(message)}\n" // + + ""; List textEdits = registry.getEditsForAllActiveCleanUps(new TextDocumentIdentifier(uri), Arrays.asList("invertEquals"), monitor); - assertEquals(1, textEdits.size()); - assertEquals(te("\"text\".equals(message);\n" + // - " boolean result2 = \"text\".equalsIgnoreCase(message", r(3, 22, 4, 53)), // - textEdits.get(0)); + String actual = TextEditUtil.apply(unit, textEdits); + assertEquals(expected, actual); + } + + @Test + public void testAddFinalModifiersWherePossible() throws Exception { + String contents = "package test1;\n" // + + "\n" // + + "public class AddModifier {\n" // + + "\n" // + + "private String label1;\n" // + + "protected String label2;\n" // + + "public String label3;\n" // + + "private String label4 = \"\";\n" // + + "protected String label5 = \"\";\n" // + + "public String label6 = \"\";\n" // + + "private final String label7 = \"\";\n" // + + "\n" // + + "public void test(String foo) {\n" // + + " String label8, label9 = \"\";\n" // + + " String label10;\n" // + + " String label11 = \"\";\n" // + + " final String label12 = \"\";\n" // + + " }\n" // + + "}"; + + ICompilationUnit unit = pack1.createCompilationUnit("AddModifier.java", contents, false, monitor); + String uri = unit.getUnderlyingResource().getLocationURI().toString(); + List textEdits = registry.getEditsForAllActiveCleanUps(new TextDocumentIdentifier(uri), Arrays.asList("addFinalModifier"), monitor); + String actual = TextEditUtil.apply(unit, textEdits); + String expected = "package test1;\n" // + + "\n" // + + "public class AddModifier {\n" // + + "\n" // + + "private String label1;\n" // + + "protected String label2;\n" // + + "public String label3;\n" // + + "private final String label4 = \"\";\n" // + + "protected String label5 = \"\";\n" // + + "public String label6 = \"\";\n" // + + "private final String label7 = \"\";\n" // + + "\n" // + + "public void test(final String foo) {\n" // + + " final String label8, label9 = \"\";\n" // + + " final String label10;\n" // + + " final String label11 = \"\";\n" // + + " final String label12 = \"\";\n" // + + " }\n" // + + "}"; + + assertEquals(expected, actual); } - private static TextEdit te(String contents, Range range) { - TextEdit textEdit = new TextEdit(); - textEdit.setNewText(contents); - textEdit.setRange(range); - return textEdit; + @Test + public void testConvertToSwitchExpression() throws Exception { + String contents = "package test1;\n" // + + "\n" // + + "public class SwitchExpression {\n" // + + " public void test() {\n" // + + " Day day2 = Day.THURSDAY;\n" // + + " String message2;\n" // + + " switch (day2) {\n" // + + " case SATURDAY:\n" // + + " case SUNDAY:\n" // + + " message2 = \"Weekend!\";\n" // + + " break;\n" // + + " case MONDAY:\n" // + + " case TUESDAY:\n" // + + " case WEDNESDAY:\n" // + + " case THURSDAY:\n" // + + " case FRIDAY:\n" // + + " message2 = \"Weekday\";\n" // + + " break;\n" // + + " default:\n" // + + " message2 = \"???\";\n" // + + " }\n" // + + " }\n" // + + "\n" // + + " public enum Day {\n" // + + " MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY\n" // + + " };\n" // + + "}"; + + ICompilationUnit unit = pack1.createCompilationUnit("SwitchExpression.java", contents, false, monitor); + String uri = unit.getUnderlyingResource().getLocationURI().toString(); + List textEdits = registry.getEditsForAllActiveCleanUps(new TextDocumentIdentifier(uri), Arrays.asList("switchExpression"), monitor); + String actual = TextEditUtil.apply(unit, textEdits); + String expected = "package test1;\n" // + + "\n" // + + "public class SwitchExpression {\n" // + + " public void test() {\n" // + + " Day day2 = Day.THURSDAY;\n" // + + " String message2 = switch (day2) {\n" // + + " case SATURDAY, SUNDAY -> \"Weekend!\";\n" // + + " case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> \"Weekday\";\n" // + + " default -> \"???\";\n" // + + " };\n" // + + " }\n" // + + "\n" // + + " public enum Day {\n" // + + " MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY\n" // + + " };\n" // + + "}"; + + assertEquals(expected, actual); + } + + @Test + public void testPatternMatchInstanceof() throws Exception { + String contents = "package test1;\n" // + + "\n" // + + "public class InstanceofPatternMatch {\n" // + + " public void test() {\n" // + + " Object str = new String(\"test\");\n" // + + " if (str instanceof String) {\n" // + + " String real = (String) str;\n" // + + " System.out.println(real.substring(0));\n" // + + " }\n" // + + " }\n" // + + "}"; + + ICompilationUnit unit = pack1.createCompilationUnit("InstanceofPatternMatch.java", contents, false, monitor); + String uri = unit.getUnderlyingResource().getLocationURI().toString(); + List textEdits = registry.getEditsForAllActiveCleanUps(new TextDocumentIdentifier(uri), Arrays.asList("instanceofPatternMatch"), monitor); + String actual = TextEditUtil.apply(unit, textEdits); + String expected = "package test1;\n" // + + "\n" // + + "public class InstanceofPatternMatch {\n" // + + " public void test() {\n" // + + " Object str = new String(\"test\");\n" // + + " if (str instanceof String real) {\n" // + + " System.out.println(real.substring(0));\n" // + + " }\n" // + + " }\n" // + + "}"; + + assertEquals(expected, actual); } - private static Range r(int beginLine, int beginChar, int endLine, int endChar) { - Position start = new Position(beginLine, beginChar); - Position end = new Position(endLine, endChar); - Range range = new Range(start, end); - return range; + @Test + public void testLambdaExpression() throws Exception { + String contents = "package test1;\n" // + + "\n" // + + "import java.util.function.IntConsumer;\n" // + + "\n" // + + "public class LambdaExpression {\n" // + + " public void test() {\n" // + + " IntConsumer c = new IntConsumer() {\n" // + + " @Override\n" // + + " public void accept(int value) {\n" // + + " System.out.println(value);\n" // + + " }\n" // + + " };\n" // + + " }\n" // + + "}"; + + ICompilationUnit unit = pack1.createCompilationUnit("LambdaExpression.java", contents, false, monitor); + String uri = unit.getUnderlyingResource().getLocationURI().toString(); + List textEdits = registry.getEditsForAllActiveCleanUps(new TextDocumentIdentifier(uri), Arrays.asList("lambdaExpression"), monitor); + String actual = TextEditUtil.apply(unit, textEdits); + String expected = "package test1;\n" // + + "\n" // + + "import java.util.function.IntConsumer;\n" // + + "\n" // + + "public class LambdaExpression {\n" // + + " public void test() {\n" // + + " IntConsumer c = value -> System.out.println(value);\n" // + + " }\n" // + + "}"; + + assertEquals(expected, actual); + } + + @Test + public void testMultiCleanup() throws Exception { + String contents = "package test1;\n" + + "\n" + + "import java.io.File;\n" + + "import java.io.FileFilter;\n" + + "import java.util.Arrays;\n" + + "\n" + + "public class MutliCleanup {\n" + + " public void test() {\n" + + " String PATH = \"/this/is/some/path\";\n" + + " String MESSAGE = \"This is a message.\" +\n" + + " \"This message has multiple lines.\" +\n" + + " \"We can convert it to a text block\";\n" + + "\n" + + " Object[] obj = Arrays.asList(PATH).toArray();\n" + + " if (obj[0] instanceof String) {\n" + + " String tmp = (String) obj[0];\n" + + " File f = new File(tmp);\n" + + " File[] filtered = f.listFiles(new FileFilter() {\n" + + " @Override\n" + + " public boolean accept(File path) {\n" + + " return true;\n" + + " }\n" + + " });\n" + + " }\n" + + " }\n" + + "}\n" + + ""; + + ICompilationUnit unit = pack1.createCompilationUnit("MultiCleanup.java", contents, false, monitor); + String uri = unit.getUnderlyingResource().getLocationURI().toString(); + List textEdits = registry.getEditsForAllActiveCleanUps(new TextDocumentIdentifier(uri), Arrays.asList("lambdaExpression", "instanceofPatternMatch", "stringConcatToTextBlock", "addFinalModifier"), monitor); + String actual = TextEditUtil.apply(unit, textEdits); + String expected = "package test1;\n" + + "\n" + + "import java.io.File;\n" + + "import java.io.FileFilter;\n" + + "import java.util.Arrays;\n" + + "\n" + + "public class MutliCleanup {\n" + + " public void test() {\n" + + " final String PATH = \"/this/is/some/path\";\n" + + " final String MESSAGE = \"\"\"\n" + + " This is a message.\\\n" + + " This message has multiple lines.\\\n" + + " We can convert it to a text block\"\"\";\n" + + "\n" + + " final Object[] obj = Arrays.asList(PATH).toArray();\n" + + " if (obj[0] instanceof final String tmp) {\n" + + " final File f = new File(tmp);\n" + + " final File[] filtered = f.listFiles((FileFilter) path -> true);\n" + + " }\n" + + " }\n" + + "}\n" + + ""; + + assertEquals(expected, actual); } }