|
15 | 15 | */
|
16 | 16 | package org.openrewrite.java.migrate.net;
|
17 | 17 |
|
18 |
| -import com.google.errorprone.refaster.annotation.AfterTemplate; |
19 |
| -import com.google.errorprone.refaster.annotation.BeforeTemplate; |
20 |
| -import org.openrewrite.java.template.RecipeDescriptor; |
| 18 | +import org.jspecify.annotations.Nullable; |
| 19 | +import org.openrewrite.ExecutionContext; |
| 20 | +import org.openrewrite.Preconditions; |
| 21 | +import org.openrewrite.Recipe; |
| 22 | +import org.openrewrite.TreeVisitor; |
| 23 | +import org.openrewrite.analysis.constantfold.ConstantFold; |
| 24 | +import org.openrewrite.analysis.util.CursorUtil; |
| 25 | +import org.openrewrite.java.JavaParser; |
| 26 | +import org.openrewrite.java.JavaTemplate; |
| 27 | +import org.openrewrite.java.JavaVisitor; |
| 28 | +import org.openrewrite.java.MethodMatcher; |
| 29 | +import org.openrewrite.java.search.UsesType; |
| 30 | +import org.openrewrite.java.tree.Expression; |
| 31 | +import org.openrewrite.java.tree.J; |
| 32 | +import org.openrewrite.java.tree.JavaType; |
| 33 | +import org.openrewrite.java.tree.TypeUtils; |
21 | 34 |
|
22 | 35 | import java.net.URI;
|
23 |
| -import java.net.URL; |
24 | 36 |
|
25 |
| -public class URLConstructorsToURI { |
26 |
| - @RecipeDescriptor( |
27 |
| - name = "Convert `new URL(String)` to `URI.create(String).toURL()`", |
28 |
| - description = "Converts `new URL(String)` constructors to `URI.create(String).toURL()`." |
29 |
| - ) |
30 |
| - public static class URLSingleArgumentConstructor { |
31 |
| - @BeforeTemplate |
32 |
| - URL urlConstructor(String spec) throws Exception { |
33 |
| - return new URL(spec); |
34 |
| - } |
| 37 | +public class URLConstructorsToURI extends Recipe { |
35 | 38 |
|
36 |
| - @AfterTemplate |
37 |
| - URL uriCreateToURL(String spec) throws Exception { |
38 |
| - return URI.create(spec).toURL(); |
39 |
| - } |
40 |
| - } |
| 39 | + private static final String URI_FQN = "java.net.URI"; |
| 40 | + private static final String URL_FQN = "java.net.URL"; |
| 41 | + private static final MethodMatcher methodMatcherSingleArg = new MethodMatcher(URL_FQN + " <constructor>(java.lang.String)"); |
| 42 | + private static final MethodMatcher methodMatcherThreeArg = new MethodMatcher(URL_FQN + " <constructor>(java.lang.String, java.lang.String, java.lang.String)"); |
| 43 | + private static final MethodMatcher methodMatcherFourArg = new MethodMatcher(URL_FQN + " <constructor>(java.lang.String, java.lang.String, int, java.lang.String)"); |
41 | 44 |
|
42 |
| - @RecipeDescriptor( |
43 |
| - name = "Convert `new URL(String, String, String)` to `new URI(...).toURL()`", |
44 |
| - description = "Converts `new URL(String, String, String)` constructors to `new URI(...).toURL()`." |
45 |
| - ) |
46 |
| - public static class URLThreeArgumentConstructor { |
47 |
| - @BeforeTemplate |
48 |
| - URL urlConstructor(String protocol, String host, String file) throws Exception { |
49 |
| - return new URL(protocol, host, file); |
50 |
| - } |
| 45 | + @Override |
| 46 | + public String getDisplayName() { |
| 47 | + return "Convert `new URL(String)` to `URI.create(String).toURL()`"; |
| 48 | + } |
51 | 49 |
|
52 |
| - @AfterTemplate |
53 |
| - URL newUriToUrl(String protocol, String host, String file) throws Exception { |
54 |
| - return new URI(protocol, null, host, -1, file, null, null).toURL(); |
55 |
| - } |
| 50 | + @Override |
| 51 | + public String getDescription() { |
| 52 | + return "Converts `new URL(String)` constructors to `URI.create(String).toURL()`."; |
56 | 53 | }
|
57 | 54 |
|
58 |
| - @RecipeDescriptor( |
59 |
| - name = "Convert `new URL(String, String, int, String)` to `new URI(...).toURL()`", |
60 |
| - description = "Converts `new URL(String, String, int, String)` constructors to `new URI(...).toURL()`." |
61 |
| - ) |
62 |
| - public static class URLFourArgumentConstructor { |
63 |
| - @BeforeTemplate |
64 |
| - URL urlConstructor(String protocol, String host, int port, String file) throws Exception { |
65 |
| - return new URL(protocol, host, port, file); |
66 |
| - } |
| 55 | + @Override |
| 56 | + public TreeVisitor<?, ExecutionContext> getVisitor() { |
| 57 | + return Preconditions.check(new UsesType<>(URL_FQN, false), |
| 58 | + new JavaVisitor<ExecutionContext>() { |
| 59 | + @Override |
| 60 | + public J visitNewClass(J.NewClass nc, ExecutionContext ctx) { |
| 61 | + if (methodMatcherSingleArg.matches(nc)) { |
| 62 | + String path = extractPath(nc.getArguments().get(0)); |
| 63 | + if (isNotValidPath(path)) { |
| 64 | + return nc; |
| 65 | + } |
| 66 | + |
| 67 | + JavaTemplate template = JavaTemplate.builder("URI.create(#{any(String)}).toURL()") |
| 68 | + .imports(URI_FQN) |
| 69 | + .contextSensitive() |
| 70 | + .javaParser(JavaParser.fromJavaVersion()) |
| 71 | + .build(); |
| 72 | + maybeAddImport(URI_FQN); |
| 73 | + |
| 74 | + return template.apply(getCursor(), |
| 75 | + nc.getCoordinates().replace(), |
| 76 | + nc.getArguments().get(0)); |
| 77 | + } else { |
| 78 | + if (methodMatcherThreeArg.matches(nc)) { |
| 79 | + JavaTemplate template = JavaTemplate.builder("new URI(#{any(String)}, null, #{any(String)}, -1, #{any(String)}, null, null).toURL()") |
| 80 | + .imports(URI_FQN, URL_FQN) |
| 81 | + .contextSensitive() |
| 82 | + .javaParser(JavaParser.fromJavaVersion()) |
| 83 | + .build(); |
| 84 | + |
| 85 | + maybeAddImport(URI_FQN); |
| 86 | + return template.apply(getCursor(), nc.getCoordinates().replace(), |
| 87 | + nc.getArguments().get(0), |
| 88 | + nc.getArguments().get(1), |
| 89 | + nc.getArguments().get(2)); |
| 90 | + } else if (methodMatcherFourArg.matches(nc)) { |
| 91 | + JavaTemplate template = JavaTemplate.builder("new URI(#{any(String)}, null, #{any(String)}, #{any(int)}, #{any(String)}, null, null).toURL()") |
| 92 | + .imports(URI_FQN, URL_FQN) |
| 93 | + .contextSensitive() |
| 94 | + .javaParser(JavaParser.fromJavaVersion()) |
| 95 | + .build(); |
| 96 | + |
| 97 | + maybeAddImport(URI_FQN); |
| 98 | + return template.apply(getCursor(), nc.getCoordinates().replace(), |
| 99 | + nc.getArguments().get(0), |
| 100 | + nc.getArguments().get(1), |
| 101 | + nc.getArguments().get(2), |
| 102 | + nc.getArguments().get(3)); |
| 103 | + } |
| 104 | + } |
| 105 | + return super.visitNewClass(nc, ctx); |
| 106 | + } |
| 107 | + |
| 108 | + @Nullable |
| 109 | + private String extractPath(Expression arg) { |
| 110 | + if (arg instanceof J.Literal && |
| 111 | + TypeUtils.isOfType(arg.getType(), JavaType.Primitive.String)) { |
| 112 | + // Check if value is not null |
| 113 | + String literalValueSource = ((J.Literal) arg).getValueSource(); |
| 114 | + // Remove quotations from string |
| 115 | + return literalValueSource != null ? literalValueSource.substring(1, literalValueSource.length() - 1).trim() : null; |
| 116 | + } else if (arg instanceof J.Identifier && |
| 117 | + TypeUtils.isOfType(arg.getType(), JavaType.Primitive.String)) { |
| 118 | + // find constant value of the identifier |
| 119 | + return CursorUtil.findCursorForTree(getCursor(), arg) |
| 120 | + .bind(c -> ConstantFold.findConstantLiteralValue(c, String.class)) |
| 121 | + .toNull(); |
| 122 | + } else { |
| 123 | + // null indicates no path extractable |
| 124 | + return null; |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + private boolean isNotValidPath(@Nullable String path) { |
| 129 | + if (path == null) { |
| 130 | + return true; |
| 131 | + } |
67 | 132 |
|
68 |
| - @AfterTemplate |
69 |
| - URL newUriToUrl(String protocol, String host, int port, String file) throws Exception { |
70 |
| - return new URI(protocol, null, host, port, file, null, null).toURL(); |
71 |
| - } |
| 133 | + try { |
| 134 | + //noinspection ResultOfMethodCallIgnored |
| 135 | + URI.create(path).toURL(); |
| 136 | + return false; |
| 137 | + } catch (Exception e) { |
| 138 | + return true; |
| 139 | + } |
| 140 | + } |
| 141 | + }); |
72 | 142 | }
|
73 | 143 | }
|
0 commit comments