diff --git a/src/main/java/org/openrewrite/java/migrate/AddMissingMethodImplementation.java b/src/main/java/org/openrewrite/java/migrate/AddMissingMethodImplementation.java index 09640766a6..7e67c2bdd8 100644 --- a/src/main/java/org/openrewrite/java/migrate/AddMissingMethodImplementation.java +++ b/src/main/java/org/openrewrite/java/migrate/AddMissingMethodImplementation.java @@ -26,9 +26,12 @@ import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.TypeUtils; +import static org.openrewrite.java.tree.J.ClassDeclaration.Kind.Type.Interface; + @Value @EqualsAndHashCode(callSuper = false) public class AddMissingMethodImplementation extends Recipe { + @Option(displayName = "Fully Qualified Class Name", description = "A fully qualified class being implemented with missing method.", example = "com.yourorg.FooBar") @@ -49,7 +52,7 @@ public class AddMissingMethodImplementation extends Recipe { @Override public String getDisplayName() { - return "Adds missing method implementations."; + return "Adds missing method implementations"; } @Override @@ -63,6 +66,7 @@ public TreeVisitor getVisitor() { } public class ClassImplementationVisitor extends JavaIsoVisitor { + private final JavaTemplate methodTemplate = JavaTemplate.builder(methodTemplateString).build(); private final MethodMatcher methodMatcher = new MethodMatcher(methodPattern, true); @@ -71,8 +75,8 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration cs, Execution // need to make sure we handle sub-classes J.ClassDeclaration classDecl = super.visitClassDeclaration(cs, ctx); - // No need to make changes to abstract classes; only change concrete classes. - if (classDecl.hasModifier(J.Modifier.Type.Abstract)) { + // No need to make changes to abstract classes or interfaces; only change concrete classes. + if (classDecl.hasModifier(J.Modifier.Type.Abstract) || classDecl.getKind() == Interface) { return classDecl; } // Don't make changes to classes that don't match the fully qualified name diff --git a/src/main/resources/META-INF/rewrite/java-version-11.yml b/src/main/resources/META-INF/rewrite/java-version-11.yml index 07f395fbd8..fdc13248d3 100644 --- a/src/main/resources/META-INF/rewrite/java-version-11.yml +++ b/src/main/resources/META-INF/rewrite/java-version-11.yml @@ -184,13 +184,19 @@ recipeList: newGroupId: jakarta.xml.ws newArtifactId: jakarta.xml.ws-api newVersion: 2.3.x - # Add the jakarta JAXB artifact if it is missing but a project uses types in java.xml.bind + # Add the jakarta JAXB artifact if it is missing but a project uses types in either javax.jws or javax.xml.ws - org.openrewrite.java.dependencies.AddDependency: groupId: jakarta.xml.ws artifactId: jakarta.xml.ws-api version: 2.3.x onlyIfUsing: javax.jws..* acceptTransitive: true + - org.openrewrite.java.dependencies.AddDependency: + groupId: jakarta.xml.ws + artifactId: jakarta.xml.ws-api + version: 2.3.x + onlyIfUsing: javax.xml.ws..* + acceptTransitive: true # If a project already had the jakarta api, make sure it is at the latest version. - org.openrewrite.java.dependencies.UpgradeDependencyVersion: groupId: jakarta.xml.ws diff --git a/src/main/resources/META-INF/rewrite/java-version-7.yml b/src/main/resources/META-INF/rewrite/java-version-7.yml index e12d6edc2f..cb5f4e364c 100644 --- a/src/main/resources/META-INF/rewrite/java-version-7.yml +++ b/src/main/resources/META-INF/rewrite/java-version-7.yml @@ -45,7 +45,7 @@ recipeList: methodTemplateString: "public java.lang.String getSchema() { \n\t// TODO Auto-generated method stub\n return null; }" - org.openrewrite.java.migrate.AddMissingMethodImplementation: fullyQualifiedClassName: java.sql.Connection - methodPattern: "*..* setNetworkTimeout(java.util.concurrent.Executor)" + methodPattern: "*..* setNetworkTimeout(java.util.concurrent.Executor, int)" methodTemplateString: "public void setNetworkTimeout(java.util.concurrent.Executor executor, int milliseconds) { \n\t// TODO Auto-generated method stub\n }" - org.openrewrite.java.migrate.AddMissingMethodImplementation: fullyQualifiedClassName: java.sql.Connection diff --git a/src/test/java/org/openrewrite/java/migrate/AddMissingMethodImplementationTest.java b/src/test/java/org/openrewrite/java/migrate/AddMissingMethodImplementationTest.java new file mode 100644 index 0000000000..1e0d1e25d1 --- /dev/null +++ b/src/test/java/org/openrewrite/java/migrate/AddMissingMethodImplementationTest.java @@ -0,0 +1,136 @@ +/* + * Copyright 2024 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.migrate; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.Issue; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.java.Assertions.javaVersion; + +class AddMissingMethodImplementationTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe( + new AddMissingMethodImplementation("I1", "*..* m1()", + "public void m1() { System.out.println(\"m1\"); }")) + .allSources(src -> src.markers(javaVersion(21))); + } + + @Test + @Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/459") + void skipInterfaces() { + //language=java + rewriteRun( + java( + """ + interface I1 {} + interface I2 extends I1 {} + """ + ) + ); + } + + @Test + void skipAbstractClasses() { + //language=java + rewriteRun( + java( + """ + interface I1 {} + abstract class AC2 implements I1 {} + """ + ) + ); + } + + @DocumentExample + @Test + void happyPath() { + //language=java + rewriteRun( + java( + """ + interface I1 {} + class C2 implements I1 {} + """, + """ + interface I1 {} + class C2 implements I1 { + public void m1() { + System.out.println("m1"); + }} + """ + ) + ); + } + + @Test + void methodExists() { + //language=java + rewriteRun( + java( + """ + interface I1 {} + class C2 implements I1 { + public void m1() { + System.out.println("m1"); + } + } + """ + ) + ); + } + + @Test + void methodExistsDiffImpl() { + //language=java + rewriteRun( + java( + """ + interface I1 {} + class C2 implements I1 { + public void m1() { + System.out.println("m1 diff"); + } + } + """ + ) + ); + } + + @Test + void methodExistsDiffSignature() { + //language=java + rewriteRun( + java( + """ + interface I1 {} + class C2 implements I1 { + protected void m1() { + System.out.println("m1"); + } + } + """ + ) + ); + } + +} diff --git a/src/test/java/org/openrewrite/java/migrate/UpgradeToJava7Test.java b/src/test/java/org/openrewrite/java/migrate/UpgradeToJava7Test.java index 53646a1d19..183bbbb4e7 100644 --- a/src/test/java/org/openrewrite/java/migrate/UpgradeToJava7Test.java +++ b/src/test/java/org/openrewrite/java/migrate/UpgradeToJava7Test.java @@ -41,7 +41,7 @@ void callableStatement() { rewriteRun( //language=java java( - """ + """ package com.test.withoutV170Methods; import java.io.InputStream; @@ -2033,7 +2033,7 @@ void connectionPoolDataSource() { rewriteRun( //language=java java( - """ + """ package com.test.withoutV170Methods; import javax.sql.ConnectionPoolDataSource; @@ -2123,7 +2123,7 @@ void commonDataSource() { rewriteRun( //language=java java( - """ + """ package com.test.withoutV170Methods; import javax.sql.CommonDataSource; @@ -2199,7 +2199,7 @@ void commonDataSourceSub() { rewriteRun( //language=java java( - """ + """ package com.test.withoutV170Methods; import javax.sql.CommonDataSource; @@ -2271,37 +2271,37 @@ void onAbstractClassWithMissingMethod() { rewriteRun( //language=java java( - """ - package com.test.withoutV170Methods; - - import javax.sql.CommonDataSource; - - import java.io.PrintWriter; - import java.sql.SQLException; - - - public abstract class JRE7JdbcCommonDataSource implements CommonDataSource { - - public int getLoginTimeout() throws SQLException { - return 0; - } - - public PrintWriter getLogWriter() throws SQLException { - return null; - } - - public void setLoginTimeout(int seconds) throws SQLException { - - - } - - public void setLogWriter(PrintWriter out) throws SQLException { - - - } - - } """ + package com.test.withoutV170Methods; + + import javax.sql.CommonDataSource; + + import java.io.PrintWriter; + import java.sql.SQLException; + + + public abstract class JRE7JdbcCommonDataSource implements CommonDataSource { + + public int getLoginTimeout() throws SQLException { + return 0; + } + + public PrintWriter getLogWriter() throws SQLException { + return null; + } + + public void setLoginTimeout(int seconds) throws SQLException { + + + } + + public void setLogWriter(PrintWriter out) throws SQLException { + + + } + + } + """ ) ); } @@ -2312,7 +2312,7 @@ void connection() { spec -> spec.cycles(1).expectedCyclesThatMakeChanges(1), //language=java java( - """ + """ package com.test.withoutV170Methods; import java.sql.*; @@ -2790,12 +2790,264 @@ public void setSchema(java.lang.String schema) throws java.sql.SQLException { ); } + @Test + void connectionWithMethodsPresent() { + rewriteRun( + //language=java + java( + """ + package com.test.withoutV170Methods; + + import java.sql.*; + import java.util.Map; + import java.util.Properties; + + import javax.sql.*; + + + public class JRE7JdbcConnection implements java.sql.Connection { + + public T unwrap(Class iface) throws SQLException { + return null; + } + + public boolean isWrapperFor(Class iface) throws SQLException { + return false; + } + + public void clearWarnings() throws SQLException { + + } + + public void close() throws SQLException { + + } + + public void commit() throws SQLException { + + } + + public Statement createStatement() throws SQLException { + return null; + } + + public Statement createStatement(int resultSetType, int resultSetConcurrency) + throws SQLException { + return null; + } + + public Statement createStatement(int resultSetType, + int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + return null; + } + + public boolean getAutoCommit() throws SQLException { + return false; + } + + public String getCatalog() throws SQLException { + return null; + } + + public int getHoldability() throws SQLException { + return 0; + } + + public DatabaseMetaData getMetaData() throws SQLException { + return null; + } + + public int getTransactionIsolation() throws SQLException { + return 0; + } + + public Map> getTypeMap() throws SQLException { + return null; + } + + public SQLWarning getWarnings() throws SQLException { + return null; + } + + public boolean isClosed() throws SQLException { + return false; + } + + public boolean isReadOnly() throws SQLException { + return false; + } + + public String nativeSQL(String sql) throws SQLException { + return null; + } + + public CallableStatement prepareCall(String sql) throws SQLException { + return null; + } + + public CallableStatement prepareCall(String sql, int resultSetType, + int resultSetConcurrency) throws SQLException { + return null; + } + + public CallableStatement prepareCall(String sql, int resultSetType, + int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + return null; + } + + public PreparedStatement prepareStatement(String sql) throws SQLException { + return null; + } + + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) + throws SQLException { + return null; + } + + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) + throws SQLException { + return null; + } + + public PreparedStatement prepareStatement(String sql, int resultSetType, + int resultSetConcurrency) throws SQLException { + return null; + } + + public PreparedStatement prepareStatement(String sql, int resultSetType, + int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + return null; + } + + public PreparedStatement prepareStatement(String sql, String[] columnNames) + throws SQLException { + return null; + } + + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + + } + + public void rollback() throws SQLException { + + } + + public void rollback(Savepoint savepoint) throws SQLException { + + } + + public void setAutoCommit(boolean autoCommit) throws SQLException { + + } + + public void setCatalog(String catalog) throws SQLException { + + } + + public void setHoldability(int holdability) throws SQLException { + + } + + public void setReadOnly(boolean readOnly) throws SQLException { + + } + + public Savepoint setSavepoint() throws SQLException { + return null; + } + + public Savepoint setSavepoint(String name) throws SQLException { + return null; + } + + public void setTransactionIsolation(int level) throws SQLException { + + } + + public void setTypeMap(Map> map) throws SQLException { + + } + + public Clob createClob() throws SQLException { + return null; + } + + public Blob createBlob() throws SQLException { + + return null; + } + + public NClob createNClob() throws SQLException { + return null; + } + + public SQLXML createSQLXML() throws SQLException { + return null; + } + + public boolean isValid(int timeout) throws SQLException { + return false; + } + + public void setClientInfo(String name, String value) throws SQLClientInfoException { + + } + + public void setClientInfo(Properties properties) + throws SQLClientInfoException { + + } + + public String getClientInfo(String name) throws SQLException { + return null; + } + + public Properties getClientInfo() throws SQLException { + return null; + } + + public Array createArrayOf(String typeName, Object[] elements) + throws SQLException { + return null; + } + + public Struct createStruct(String typeName, Object[] attributes) + throws SQLException { + return null; + } + + public void abort(java.util.concurrent.Executor executor) { + } + + public int getNetworkTimeout() { + return 0; + } + + public java.lang.String getSchema() { + return null; + } + + public void setNetworkTimeout(java.util.concurrent.Executor executor, int milliseconds) { + } + + public void setSchema(java.lang.String schema) throws java.sql.SQLException { + } + + } + """ + ) + ); + } + @Test void dataSource() { rewriteRun( //language=java java( - """ + """ package com.test.withoutV170Methods; import javax.sql.DataSource; @@ -2909,7 +3161,7 @@ void jre7JdbcDriver() { rewriteRun( //language=java java( - """ + """ package com.test.withoutV170Methods; import java.sql.Connection; @@ -3001,7 +3253,7 @@ void preparedStatement() { rewriteRun( //language=java java( - """ + """ package com.test.withoutV170Methods; import java.io.InputStream; diff --git a/src/test/java/org/openrewrite/java/migrate/javax/AddJaxwsDependenciesTest.java b/src/test/java/org/openrewrite/java/migrate/javax/AddJaxwsDependenciesTest.java index b4b0e14387..db0c7da210 100644 --- a/src/test/java/org/openrewrite/java/migrate/javax/AddJaxwsDependenciesTest.java +++ b/src/test/java/org/openrewrite/java/migrate/javax/AddJaxwsDependenciesTest.java @@ -15,7 +15,6 @@ */ package org.openrewrite.java.migrate.javax; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.openrewrite.config.Environment; import org.openrewrite.test.RecipeSpec; @@ -29,7 +28,6 @@ import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi; import static org.openrewrite.maven.Assertions.pomXml; -@Disabled class AddJaxwsDependenciesTest implements RewriteTest { @Override @@ -173,13 +171,11 @@ void removeReferenceImplementationRuntime() { } dependencies { - compileOnly "com.sun.xml.ws:jaxws-rt:%s" + implementation "com.sun.xml.ws:jaxws-rt:%s" implementation "jakarta.xml.ws:jakarta.xml.ws-api:%s" - - testImplementation "com.sun.xml.ws:jaxws-rt:%s" } - """.formatted(rtVersion, wsApiVersion, rtVersion); + """.formatted(rtVersion, wsApiVersion); }) ), pomXml( @@ -276,13 +272,11 @@ void renameAndUpdateApiAndRuntime() { } dependencies { - compileOnly "com.sun.xml.ws:jaxws-rt:%s" + implementation "com.sun.xml.ws:jaxws-rt:%s" implementation "jakarta.xml.ws:jakarta.xml.ws-api:%s" - - testImplementation "com.sun.xml.ws:jaxws-rt:%s" } - """.formatted(rtVersion, wsApiVersion, rtVersion); + """.formatted(rtVersion, wsApiVersion); }) ), pomXml(