All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openrewrite.java.JavaTemplateTest.kt Maven / Gradle / Ivy

/*
 * Copyright 2020 the original author or authors.
 * 

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* https://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.openrewrite.java import com.google.common.io.CharSink import com.google.common.io.CharSource import com.google.googlejavaformat.java.Formatter import com.google.googlejavaformat.java.JavaFormatterOptions import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.openrewrite.ExecutionContext import org.openrewrite.Issue import org.openrewrite.java.tree.J import org.openrewrite.java.tree.JavaType import org.openrewrite.java.tree.Space import java.io.ByteArrayOutputStream import java.io.OutputStreamWriter import java.util.Comparator.comparing import java.util.function.Consumer @Suppress("Convert2MethodRef") interface JavaTemplateTest : JavaRecipeTest { @Test fun replacePackage(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "b").build() override fun visitPackage(pkg: J.Package, p: ExecutionContext): J.Package { if (pkg.expression.printTrimmed() == "a") { return pkg.withTemplate(t, pkg.coordinates.replace()) } return super.visitPackage(pkg, p) } override fun visitClassDeclaration( classDecl: J.ClassDeclaration, p: ExecutionContext, ): J.ClassDeclaration { var cd = super.visitClassDeclaration(classDecl, p) if (classDecl.type!!.packageName == "a") { cd = cd.withType(cd.type!!.withFullyQualifiedName("b.${cd.simpleName}")) } return cd } } }, before = """ package a; class Test { } """, after = """ package b; class Test { } """ ) @Test fun replaceMethod(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "int test2(int n) { return n; }").build() override fun visitMethodDeclaration( method: J.MethodDeclaration, p: ExecutionContext, ): J.MethodDeclaration { if (method.simpleName == "test") { return method.withTemplate(t, method.coordinates.replace()) } return super.visitMethodDeclaration(method, p) } } }, before = """ class Test { void test() { } } """, after = """ class Test { int test2(int n) { return n; } } """, afterConditions = { cu -> val methodType = (cu.classes.first().body.statements.first() as J.MethodDeclaration).type!! assertThat(methodType.resolvedSignature?.returnType).isEqualTo(JavaType.Primitive.Int) assertThat(methodType.resolvedSignature?.paramTypes).containsExactly(JavaType.Primitive.Int) assertThat(methodType.genericSignature?.returnType).isEqualTo(JavaType.Primitive.Int) assertThat(methodType.genericSignature?.paramTypes).containsExactly(JavaType.Primitive.Int) } ) @Test fun replaceLambdaWithMethodReference(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaVisitor() { val t = JavaTemplate.builder({ cursor }, "Object::toString").build() override fun visitLambda(lambda: J.Lambda, p: ExecutionContext): J { return lambda.withTemplate(t, lambda.coordinates.replace()) } } }, before = """ import java.util.function.Function; class Test { Function toString = it -> it.toString(); } """, after = """ import java.util.function.Function; class Test { Function toString = Object::toString; } """ ) @Test fun replaceMethodInvocationWithArray(jp: JavaParser) = assertChanged( jp, dependsOn = arrayOf(""" package org.openrewrite; public class Test { public void method(int[] val) {} public void method(int[] val1, String val2) {} } """.trimIndent()), recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "#{anyArray(int)}").build() override fun visitMethodInvocation( method: J.MethodInvocation, p: ExecutionContext ): J.MethodInvocation { var m: J.MethodInvocation = super.visitMethodInvocation(method, p) if (m.simpleName.equals("method") && m.arguments.size == 2) { m = m.withTemplate(t, m.coordinates.replaceArguments(), m.arguments[0]) } return m } } }, typeValidation = { identifiers = false }, before = """ import org.openrewrite.Test; class A { public void method() { Test test = new Test(); int[] arr = new int[]{}; test.method(arr, null); } } """, after = """ import org.openrewrite.Test; class A { public void method() { Test test = new Test(); int[] arr = new int[]{}; test.method(arr); } } """ ) @Issue("https://github.com/openrewrite/rewrite/issues/602") @Test fun replaceMethodInvocationWithMethodReference(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaVisitor() { val t = JavaTemplate.builder({ cursor }, "Object::toString").build() override fun visitMethodInvocation(method: J.MethodInvocation, p: ExecutionContext): J { return method.withTemplate(t, method.coordinates.replace()) } } }, before = """ import java.util.function.Function; class Test { Function toString = getToString(); static Function getToString() { return Object::toString; } } """, after = """ import java.util.function.Function; class Test { Function toString = Object::toString; static Function getToString() { return Object::toString; } } """ ) @Test fun replaceMethodParameters(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "int m, java.util.List n") .doBeforeParseTemplate(print) .build() override fun visitMethodDeclaration( method: J.MethodDeclaration, p: ExecutionContext ): J.MethodDeclaration { if (method.simpleName == "test" && method.parameters.size == 1) { // insert in outer method val m: J.MethodDeclaration = method.withTemplate(t, method.coordinates.replaceParameters()) val newRunnable = (method.body!!.statements[0] as J.NewClass) // insert in inner method val innerMethod = (newRunnable.body!!.statements[0] as J.MethodDeclaration) return m.withTemplate(t, innerMethod.coordinates.replaceParameters()) } return super.visitMethodDeclaration(method, p) } } }, before = """ class Test { void test() { new Runnable() { void inner() { } }; } } """, after = """ class Test { void test(int m, java.util.List n) { new Runnable() { void inner(int m, java.util.List n) { } }; } } """, afterConditions = { cu -> val type = (cu.classes.first().body.statements.first() as J.MethodDeclaration).type!! assertThat(type.paramNames) .`as`("Changing the method's parameters should have also updated its type's parameter names") .containsExactly("m", "n") assertThat(type.resolvedSignature!!.paramTypes[0]) .`as`("Changing the method's parameters should have resulted in the first parameter's type being 'int'") .isEqualTo(JavaType.Primitive.Int) assertThat(type.resolvedSignature!!.paramTypes[1]) .`as`("Changing the method's parameters should have resulted in the second parameter's type being 'List'") .matches { it is JavaType.Parameterized && it.type.fullyQualifiedName == "java.util.List" && it.typeParameters.size == 1 && it.typeParameters.first().asFullyQualified()!!.fullyQualifiedName == "java.lang.String" } } ) @Test fun replaceMethodParametersVariadicArray(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "Object[]... values") .doBeforeParseTemplate(print) .build() override fun visitMethodDeclaration( method: J.MethodDeclaration, p: ExecutionContext ): J.MethodDeclaration { if (method.simpleName == "test" && method.parameters.firstOrNull() is J.Empty) { // insert in outer method val m: J.MethodDeclaration = method.withTemplate(t, method.coordinates.replaceParameters()) val newRunnable = (method.body!!.statements[0] as J.NewClass) // insert in inner method val innerMethod = (newRunnable.body!!.statements[0] as J.MethodDeclaration) return m.withTemplate(t, innerMethod.coordinates.replaceParameters()) } return super.visitMethodDeclaration(method, p) } } }, before = """ class Test { void test() { new Runnable() { void inner() { } }; } } """, after = """ class Test { void test(Object[]... values) { new Runnable() { void inner(Object[]... values) { } }; } } """, afterConditions = { cu -> val type = (cu.classes.first().body.statements.first() as J.MethodDeclaration).type!! assertThat(type.paramNames) .`as`("Changing the method's parameters should have also updated its type's parameter names") .containsExactly("values") assertThat(type.resolvedSignature!!.paramTypes[0]) .`as`("Changing the method's parameters should have resulted in the first parameter's type being 'Object[]'") .matches { it is JavaType.Array && it.elemType.hasElementType("java.lang.Object") } } ) @Test fun replaceAndInterpolateMethodParameters(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "int n, #{}") .doBeforeParseTemplate(print) .build() override fun visitMethodDeclaration( method: J.MethodDeclaration, p: ExecutionContext ): J.MethodDeclaration { if (method.simpleName == "test" && method.parameters.size == 1) { return method.withTemplate(t, method.coordinates.replaceParameters(), method.parameters[0]) } return method } } }, before = """ class Test { void test(String s) { } } """, after = """ class Test { void test(int n, String s) { } } """, afterConditions = { cu -> val type = (cu.classes.first().body.statements.first() as J.MethodDeclaration).type!! assertThat(type.paramNames) .`as`("Changing the method's parameters should have also updated its type's parameter names") .containsExactly("n", "s") assertThat(type.resolvedSignature!!.paramTypes[0]) .`as`("Changing the method's parameters should have resulted in the first parameter's type being 'int'") .isEqualTo(JavaType.Primitive.Int) assertThat(type.resolvedSignature!!.paramTypes[1]) .`as`("Changing the method's parameters should have resulted in the second parameter's type being 'List'") .matches { it is JavaType.FullyQualified && it.fullyQualifiedName == "java.lang.String" } } ) @Test fun replaceLambdaParameters(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "int m, int n") .doBeforeParseTemplate(print) .build() override fun visitLambda(lambda: J.Lambda, p: ExecutionContext): J.Lambda = if (lambda.parameters.parameters.size == 1) { lambda.withTemplate(t, lambda.parameters.coordinates.replace()) } else { super.visitLambda(lambda, p) } } }, before = """ class Test { void test() { Object o = () -> 1; } } """, after = """ class Test { void test() { Object o = (int m, int n) -> 1; } } """ ) @Test fun replaceSingleStatement(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaVisitor() { val t = JavaTemplate.builder({ cursor }, "if(n != 1) {\n" + " n++;\n" + "}" ) .doBeforeParseTemplate(print) .build() override fun visitAssert(_assert: J.Assert, p: ExecutionContext): J = _assert.withTemplate(t, _assert.coordinates.replace()) } }, before = """ class Test { int n; void test() { assert n == 0; } } """, after = """ class Test { int n; void test() { if (n != 1) { n++; } } } """ ) @Test fun replaceStatementInBlock(jp: JavaParser.Builder<*, *>) = assertChanged( jp.logCompilationWarningsAndErrors(true).build(), recipe = toRecipe { object : JavaVisitor() { val t = JavaTemplate.builder({ cursor }, "n = 2;\nn = 3;") .doBeforeParseTemplate(print) .build() override fun visitMethodDeclaration(method: J.MethodDeclaration, p: ExecutionContext): J { val statement = method.body!!.statements[1] if (statement is J.Unary) { return method.withTemplate(t, statement.coordinates.replace()) } return method } } }, before = """ class Test { int n; void test() { n = 1; n++; } } """, after = """ class Test { int n; void test() { n = 1; n = 2; n = 3; } } """ ) @Test fun beforeStatementInBlock(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaVisitor() { val t = JavaTemplate.builder({ cursor }, "assert n == 0;") .doBeforeParseTemplate(print) .build() override fun visitMethodDeclaration(method: J.MethodDeclaration, p: ExecutionContext): J { val statement = method.body!!.statements[0] if (statement is J.Assignment) { return method.withTemplate(t, statement.coordinates.before()) } return method } } }, before = """ class Test { int n; void test() { n = 1; } } """, after = """ class Test { int n; void test() { assert n == 0; n = 1; } } """ ) @Test fun afterStatementInBlock(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaVisitor() { val t = JavaTemplate.builder({ cursor }, "n = 1;") .doBeforeParseTemplate(print) .build() override fun visitMethodDeclaration(method: J.MethodDeclaration, p: ExecutionContext): J { if (method.body!!.statements.size == 1) { return method.withTemplate(t, method.body!!.statements[0].coordinates.after()) } return method } } }, before = """ class Test { int n; void test() { assert n == 0; } } """, after = """ class Test { int n; void test() { assert n == 0; n = 1; } } """ ) @Test fun lastStatementInClassBlock(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaVisitor() { val t = JavaTemplate.builder({ cursor }, "int n;") .doBeforeParseTemplate(print) .build() override fun visitClassDeclaration(classDecl: J.ClassDeclaration, p: ExecutionContext): J { if (classDecl.body.statements.isEmpty()) { return classDecl.withTemplate(t, classDecl.body.coordinates.lastStatement()) } return classDecl } } }, before = """ class Test { } """, after = """ class Test { int n; } """ ) @Test fun lastStatementInMethodBlock(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaVisitor() { val t = JavaTemplate.builder({ cursor }, "n = 1;") .doBeforeParseTemplate(print) .build() override fun visitMethodDeclaration(method: J.MethodDeclaration, p: ExecutionContext): J { if (method.body!!.statements.size == 1) { return method.withTemplate(t, method.body!!.coordinates.lastStatement()) } return method } } }, before = """ class Test { int n; void test() { assert n == 0; } } """, after = """ class Test { int n; void test() { assert n == 0; n = 1; } } """ ) @Test fun replaceStatementRequiringNewImport(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaVisitor() { val t = JavaTemplate.builder({ cursor }, "List s = null;") .imports("java.util.List") .doBeforeParseTemplate(print) .build() override fun visitAssert(_assert: J.Assert, p: ExecutionContext): J { maybeAddImport("java.util.List") return _assert.withTemplate(t, _assert.coordinates.replace()) } } }, before = """ class Test { int n; void test() { assert n == 0; } } """, after = """ import java.util.List; class Test { int n; void test() { List s = null; } } """ ) @Suppress("UnnecessaryBoxing") @Test fun replaceArguments(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "m, Integer.valueOf(n), \"foo\"") .doBeforeParseTemplate(print) .build() override fun visitMethodInvocation( method: J.MethodInvocation, p: ExecutionContext ): J.MethodInvocation { if (method.arguments.size == 1) { return method.withTemplate(t, method.coordinates.replaceArguments()) } return method } } }, before = """ abstract class Test { abstract void test(); abstract void test(int m, int n, String foo); void fred(int m, int n, String foo) { test(); } } """, after = """ abstract class Test { abstract void test(); abstract void test(int m, int n, String foo); void fred(int m, int n, String foo) { test(m, Integer.valueOf(n), "foo"); } } """, afterConditions = { cu -> val m = (cu.classes[0].body.statements[2] as J.MethodDeclaration).body!!.statements[0] as J.MethodInvocation val type = m.type!! assertThat(type.genericSignature!!.paramTypes[0]).isEqualTo(JavaType.Primitive.Int) assertThat(type.genericSignature!!.paramTypes[1]).isEqualTo(JavaType.Primitive.Int) assertThat(type.genericSignature!!.paramTypes[2]) .matches { (it as JavaType.FullyQualified).fullyQualifiedName.equals("java.lang.String") } } ) @Test fun replaceClassAnnotation(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "@Deprecated") .doBeforeParseTemplate(print) .build() override fun visitAnnotation(annotation: J.Annotation, p: ExecutionContext): J.Annotation { if (annotation.simpleName == "SuppressWarnings") { return annotation.withTemplate(t, annotation.coordinates.replace()) } return super.visitAnnotation(annotation, p) } } }, before = "@SuppressWarnings(\"ALL\") class Test {}", after = "@Deprecated class Test {}" ) @Test fun replaceMethodAnnotations(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "@SuppressWarnings(\"other\")") .doBeforeParseTemplate(print) .build() override fun visitMethodDeclaration( method: J.MethodDeclaration, p: ExecutionContext ): J.MethodDeclaration { if (method.leadingAnnotations.size == 0) { return method.withTemplate(t, method.coordinates.replaceAnnotations()) } return super.visitMethodDeclaration(method, p) } } }, before = """ class Test { static final String WARNINGS = "ALL"; public @SuppressWarnings(WARNINGS) Test() { } public void test1() { } public @SuppressWarnings(WARNINGS) void test2() { } } """, after = """ class Test { static final String WARNINGS = "ALL"; @SuppressWarnings("other") public Test() { } @SuppressWarnings("other") public void test1() { } @SuppressWarnings("other") public void test2() { } } """ ) @Test fun replaceClassAnnotations(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "@SuppressWarnings(\"other\")") .doBeforeParseTemplate(print) .build() override fun visitClassDeclaration( classDecl: J.ClassDeclaration, p: ExecutionContext ): J.ClassDeclaration { if (classDecl.leadingAnnotations.size == 0 && classDecl.simpleName != "Test") { return classDecl.withTemplate(t, classDecl.coordinates.replaceAnnotations()) } return super.visitClassDeclaration(classDecl, p) } } }, before = """ class Test { static final String WARNINGS = "ALL"; class Inner1 { } } """, after = """ class Test { static final String WARNINGS = "ALL"; @SuppressWarnings("other") class Inner1 { } } """ ) @Test fun replaceVariableAnnotations(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "@SuppressWarnings(\"other\")") .doBeforeParseTemplate(print) .build() override fun visitVariableDeclarations( multiVariable: J.VariableDeclarations, p: ExecutionContext, ): J.VariableDeclarations { if (multiVariable.leadingAnnotations.size == 0) { return multiVariable.withTemplate(t, multiVariable.coordinates.replaceAnnotations()) } return super.visitVariableDeclarations(multiVariable, p) } } }, before = """ class Test { void test() { // the m int m; final @SuppressWarnings("ALL") int n; } } """, after = """ class Test { void test() { // the m @SuppressWarnings("other") int m; @SuppressWarnings("other") final int n; } } """ ) @Test fun addVariableAnnotationsToVariableAlreadyAnnotated(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "@Deprecated") .doBeforeParseTemplate(print) .build() override fun visitVariableDeclarations( multiVariable: J.VariableDeclarations, p: ExecutionContext, ): J.VariableDeclarations { if (multiVariable.leadingAnnotations.size == 1) { return multiVariable.withTemplate(t, multiVariable.coordinates.addAnnotation(comparing { 0 })) } return super.visitVariableDeclarations(multiVariable, p) } } }, before = """ class Test { void test() { @SuppressWarnings("ALL") /* hello */ Boolean z; @SuppressWarnings("ALL") private final int m, a; // comment n @SuppressWarnings("ALL") int n; @SuppressWarnings("ALL") final Boolean b; @SuppressWarnings("ALL") // comment x, y private Boolean x, y; } } """, after = """ class Test { void test() { @SuppressWarnings("ALL") @Deprecated /* hello */ Boolean z; @SuppressWarnings("ALL") @Deprecated private final int m, a; // comment n @SuppressWarnings("ALL") @Deprecated int n; @SuppressWarnings("ALL") @Deprecated final Boolean b; @SuppressWarnings("ALL") @Deprecated // comment x, y private Boolean x, y; } } """ ) @Test fun addVariableAnnotationsToVariableNotAnnotated(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "@SuppressWarnings(\"ALL\")") .doBeforeParseTemplate(print) .build() override fun visitVariableDeclarations( multiVariable: J.VariableDeclarations, p: ExecutionContext, ): J.VariableDeclarations { if (multiVariable.leadingAnnotations.size == 0) { return multiVariable.withTemplate(t, multiVariable.coordinates.addAnnotation(comparing { it.simpleName })) } return super.visitVariableDeclarations(multiVariable, p) } } }, before = """ class Test { void test() { final int m; int n; } } """, after = """ class Test { void test() { @SuppressWarnings("ALL") final int m; @SuppressWarnings("ALL") int n; } } """ ) @Test fun addMethodAnnotations(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "@SuppressWarnings(\"other\")") .doBeforeParseTemplate(print) .build() override fun visitMethodDeclaration( method: J.MethodDeclaration, p: ExecutionContext ): J.MethodDeclaration { if (method.leadingAnnotations.size == 0) { return method.withTemplate(t, method.coordinates.addAnnotation(comparing { it.simpleName })) } return super.visitMethodDeclaration(method, p) } } }, before = """ class Test { static final String WARNINGS = "ALL"; public void test() { } } """, after = """ class Test { static final String WARNINGS = "ALL"; @SuppressWarnings("other") public void test() { } } """ ) @Test fun addClassAnnotations(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "@SuppressWarnings(\"other\")") .doBeforeParseTemplate(print) .build() override fun visitClassDeclaration( classDecl: J.ClassDeclaration, p: ExecutionContext ): J.ClassDeclaration { if (classDecl.leadingAnnotations.size == 0 && classDecl.simpleName != "Test") { return classDecl.withTemplate(t, classDecl.coordinates.addAnnotation(comparing { it.simpleName })) } return super.visitClassDeclaration(classDecl, p) } } }, before = """ class Test { class Inner1 { } } """, after = """ class Test { @SuppressWarnings("other") class Inner1 { } } """ ) @Test fun replaceClassImplements(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "Serializable, Closeable") .imports("java.io.*") .doBeforeParseTemplate(print) .build() override fun visitClassDeclaration( classDecl: J.ClassDeclaration, p: ExecutionContext ): J.ClassDeclaration { if (classDecl.implements == null) { maybeAddImport("java.io.Closeable") maybeAddImport("java.io.Serializable") return classDecl.withTemplate(t, classDecl.coordinates.replaceImplementsClause()) } return super.visitClassDeclaration(classDecl, p) } } }, before = """ class Test { } """, after = """ import java.io.Closeable; import java.io.Serializable; class Test implements Serializable, Closeable { } """ ) @Test fun replaceClassExtends(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "List") .imports("java.util.*") .doBeforeParseTemplate(print) .build() override fun visitClassDeclaration( classDecl: J.ClassDeclaration, p: ExecutionContext ): J.ClassDeclaration { if (classDecl.extends == null) { maybeAddImport("java.util.List") return classDecl.withTemplate(t, classDecl.coordinates.replaceExtendsClause()) } return super.visitClassDeclaration(classDecl, p) } } }, before = """ class Test { } """, after = """ import java.util.List; class Test extends List { } """ ) @Suppress("RedundantThrows") @Test fun replaceThrows(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "Exception") .doBeforeParseTemplate(print) .build() override fun visitMethodDeclaration( method: J.MethodDeclaration, p: ExecutionContext ): J.MethodDeclaration { if (method.throws == null) { return method.withTemplate(t, method.coordinates.replaceThrows()) } return super.visitMethodDeclaration(method, p) } } }, before = """ class Test { void test() {} } """, after = """ class Test { void test() throws Exception {} } """, afterConditions = { cu -> val testMethodDecl = cu.classes.first().body.statements.first() as J.MethodDeclaration assertThat(testMethodDecl.type!!.thrownExceptions.map { it.fullyQualifiedName }) .containsExactly("java.lang.Exception") } ) @Test fun replaceMethodTypeParameters(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val typeParamsTemplate = JavaTemplate.builder({ cursor }, "T, U") .doBeforeParseTemplate(print) .build() val methodArgsTemplate = JavaTemplate.builder({ cursor }, "List t, U u") .imports("java.util.List") .doBeforeParseTemplate(print) .build() override fun visitMethodDeclaration( method: J.MethodDeclaration, p: ExecutionContext ): J.MethodDeclaration { if (method.typeParameters == null) { return method.withTemplate(typeParamsTemplate, method.coordinates.replaceTypeParameters()) .withTemplate(methodArgsTemplate, method.coordinates.replaceParameters()) } return super.visitMethodDeclaration(method, p) } } }, before = """ import java.util.List; class Test { void test() { } } """, after = """ import java.util.List; class Test { void test(List t, U u) { } } """, typeValidation = { identifiers = false }, afterConditions = { cu -> val type = (cu.classes.first().body.statements.first() as J.MethodDeclaration).type!! assertThat(type).isNotNull val paramTypes = type.genericSignature!!.paramTypes assertThat(paramTypes[0]) .`as`("The method declaration's type's genericSignature first argument should have have type 'java.util.List'") .matches { tType -> tType is JavaType.FullyQualified && tType.fullyQualifiedName == "java.util.List" } assertThat(paramTypes[1]) .`as`("The method declaration's type's genericSignature second argument should have type 'U' with bound 'java.lang.Object'") .matches { uType -> uType is JavaType.GenericTypeVariable && uType.fullyQualifiedName == "U" && uType.bound!!.fullyQualifiedName == "java.lang.Object" } } ) @Test fun replaceClassTypeParameters(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, "T, U") .doBeforeParseTemplate(print) .build() override fun visitClassDeclaration( classDecl: J.ClassDeclaration, p: ExecutionContext ): J.ClassDeclaration { if (classDecl.typeParameters == null) { return classDecl.withTemplate(t, classDecl.coordinates.replaceTypeParameters()) } return super.visitClassDeclaration(classDecl, p) } } }, before = """ class Test { } """, after = """ class Test { } """ ) @Test fun replaceBody(jp: JavaParser.Builder<*, *>) = assertChanged( jp.logCompilationWarningsAndErrors(true).build(), recipe = toRecipe { object : JavaVisitor() { val t = JavaTemplate.builder({ cursor }, "n = 1;") .doBeforeParseTemplate(print) .build() override fun visitMethodDeclaration(method: J.MethodDeclaration, p: ExecutionContext): J { val statement = method.body!!.statements[0] if (statement is J.Unary) { return method.withTemplate(t, method.coordinates.replaceBody()) } return method } } }, before = """ class Test { int n; void test() { n++; } } """, after = """ class Test { int n; void test() { n = 1; } } """ ) @Test fun replaceMissingBody(jp: JavaParser.Builder<*, *>) = assertChanged( jp.logCompilationWarningsAndErrors(true).build(), recipe = toRecipe { object : JavaVisitor() { val t = JavaTemplate.builder({ cursor }, "") .doBeforeParseTemplate(print) .build() override fun visitMethodDeclaration(method: J.MethodDeclaration, p: ExecutionContext): J { var m = method if (!m.isAbstract) { return m } m = m.withReturnTypeExpression(m.returnTypeExpression!!.withPrefix(Space.EMPTY)) m = m.withModifiers(emptyList()) m = m.withTemplate(t, m.coordinates.replaceBody()) return m } } }, before = """ abstract class Test { abstract void test(); } """, after = """ abstract class Test { void test(){ } } """ ) @Issue("https://github.com/openrewrite/rewrite/issues/793") @Test fun replaceClassAnnotationsWithAnnotationWithComment(jp: JavaParser) = assertChanged( jp, recipe = toRecipe { object : JavaIsoVisitor() { val t = JavaTemplate.builder({ cursor }, """ /** * Do suppress those warnings */ @SuppressWarnings("other") """.trimIndent()) .doBeforeParseTemplate(print) .build() override fun visitClassDeclaration( classDecl: J.ClassDeclaration, p: ExecutionContext ): J.ClassDeclaration { val cd = super.visitClassDeclaration(classDecl, p) if (cd.leadingAnnotations.size == 0) { return cd.withTemplate(t, cd.coordinates.replaceAnnotations()) } return cd } } }, before = """ class Test { class Inner1 { } } """, after = """ /** * Do suppress those warnings */ @SuppressWarnings("other") class Test { /** * Do suppress those warnings */ @SuppressWarnings("other") class Inner1 { } } """ ) companion object { val print = Consumer { s: String -> try { val bos = ByteArrayOutputStream() Formatter(JavaFormatterOptions.builder().style(JavaFormatterOptions.Style.AOSP).build()) .formatSource(CharSource.wrap(s), object : CharSink() { override fun openStream() = OutputStreamWriter(bos) }) // println(bos.toString(Charsets.UTF_8).trim()) } catch (_: Throwable) { // println("Unable to format:") // println(s) } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy