From 1e7e6666c3f85504168bf7e4c6491eefb8a964db Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Wed, 7 Aug 2024 14:51:12 -0500 Subject: [PATCH] Add GradleDependency trait --- .../gradle/trait/GradleDependency.java | 167 ++++++++++++++ .../gradle/trait/GradleTraitMatcher.java | 37 ++++ .../gradle/trait/package-info.java | 21 ++ .../gradle/trait/GradleDependencyTest.java | 207 ++++++++++++++++++ .../org/openrewrite/gradle/trait/Traits.java | 25 +++ 5 files changed, 457 insertions(+) create mode 100644 rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java create mode 100644 rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleTraitMatcher.java create mode 100644 rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/package-info.java create mode 100644 rewrite-gradle/src/test/java/org/openrewrite/gradle/trait/GradleDependencyTest.java create mode 100644 rewrite-gradle/src/test/java/org/openrewrite/gradle/trait/Traits.java diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java new file mode 100644 index 00000000000..f2a285cb743 --- /dev/null +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java @@ -0,0 +1,167 @@ +/* + * 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.gradle.trait; + +import lombok.Getter; +import lombok.Value; +import org.openrewrite.Cursor; +import org.openrewrite.gradle.marker.GradleDependencyConfiguration; +import org.openrewrite.gradle.marker.GradleProject; +import org.openrewrite.gradle.util.DependencyStringNotationConverter; +import org.openrewrite.groovy.tree.G; +import org.openrewrite.internal.lang.Nullable; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; +import org.openrewrite.maven.tree.Dependency; +import org.openrewrite.maven.tree.ResolvedDependency; +import org.openrewrite.trait.Trait; + +import java.util.List; + +import static org.openrewrite.internal.StringUtils.matchesGlob; + +@Value +public class GradleDependency implements Trait { + Cursor cursor; + + @Getter + ResolvedDependency resolvedDependency; + + public static class Matcher extends GradleTraitMatcher { + @Nullable + protected String groupId; + + @Nullable + protected String artifactId; + + public Matcher groupId(String groupId) { + this.groupId = groupId; + return this; + } + + public Matcher artifactId(String artifactId) { + this.artifactId = artifactId; + return this; + } + + @Override + protected @Nullable GradleDependency test(Cursor cursor) { + Object object = cursor.getValue(); + if (object instanceof J.MethodInvocation) { + J.MethodInvocation methodInvocation = (J.MethodInvocation) object; + + GradleProject gradleProject = getGradleProject(cursor); + if (gradleProject == null) { + return null; + } + + GradleDependencyConfiguration configuration = gradleProject.getConfiguration(methodInvocation.getSimpleName()); + if (configuration == null) { + return null; + } + + org.openrewrite.gradle.util.Dependency dependency = null; + Expression argument = methodInvocation.getArguments().get(0); + if (argument instanceof J.Literal || argument instanceof G.GString || argument instanceof G.MapEntry) { + dependency = parseDependency(methodInvocation.getArguments()); + } else if (argument instanceof J.MethodInvocation && + (((J.MethodInvocation) argument).getSimpleName().equals("platform") || + ((J.MethodInvocation) argument).getSimpleName().equals("enforcedPlatform"))) { + dependency = parseDependency(((J.MethodInvocation) argument).getArguments()); + } + + if (dependency == null) { + return null; + } + + if (configuration.isCanBeResolved()) { + for (ResolvedDependency resolvedDependency : configuration.getResolved()) { + if ((groupId == null || matchesGlob(resolvedDependency.getGroupId(), groupId)) && + (artifactId == null || matchesGlob(resolvedDependency.getArtifactId(), artifactId))) { + Dependency req = resolvedDependency.getRequested(); + if ((req.getGroupId() == null || req.getGroupId().equals(dependency.getGroupId())) && + req.getArtifactId().equals(dependency.getArtifactId())) { + return new GradleDependency(cursor, resolvedDependency); + } + } + } + } else { + for (GradleDependencyConfiguration transitiveConfiguration : gradleProject.configurationsExtendingFrom(configuration, true)) { + if (transitiveConfiguration.isCanBeResolved()) { + for (ResolvedDependency resolvedDependency : transitiveConfiguration.getResolved()) { + if ((groupId == null || matchesGlob(resolvedDependency.getGroupId(), groupId)) && + (artifactId == null || matchesGlob(resolvedDependency.getArtifactId(), artifactId))) { + Dependency req = resolvedDependency.getRequested(); + if ((req.getGroupId() == null || req.getGroupId().equals(dependency.getGroupId())) && + req.getArtifactId().equals(dependency.getArtifactId())) { + return new GradleDependency(cursor, resolvedDependency); + } + } + } + } + } + } + } + + return null; + } + + private @Nullable org.openrewrite.gradle.util.Dependency parseDependency(List arguments) { + Expression argument = arguments.get(0); + if (argument instanceof J.Literal) { + return DependencyStringNotationConverter.parse((String) ((J.Literal) argument).getValue()); + } else if (argument instanceof G.GString) { + G.GString gstring = (G.GString) argument; + List strings = gstring.getStrings(); + if (strings.size() >= 2 && strings.get(0) instanceof J.Literal && ((J.Literal) strings.get(0)).getValue() != null) { + return DependencyStringNotationConverter.parse((String) ((J.Literal) strings.get(0)).getValue()); + } + } else if (argument instanceof G.MapEntry) { + String group = null; + String artifact = null; + + for (Expression e : arguments) { + if (!(e instanceof G.MapEntry)) { + continue; + } + G.MapEntry arg = (G.MapEntry) e; + if (!(arg.getKey() instanceof J.Literal) || !(arg.getValue() instanceof J.Literal)) { + continue; + } + J.Literal key = (J.Literal) arg.getKey(); + J.Literal value = (J.Literal) arg.getValue(); + if (!(key.getValue() instanceof String) || !(value.getValue() instanceof String)) { + continue; + } + String keyValue = (String) key.getValue(); + if ("group".equals(keyValue)) { + group = (String) value.getValue(); + } else if ("name".equals(keyValue)) { + artifact = (String) value.getValue(); + } + } + + if (group == null || artifact == null) { + return null; + } + + return new org.openrewrite.gradle.util.Dependency(group, artifact, null, null, null); + } + + return null; + } + } +} diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleTraitMatcher.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleTraitMatcher.java new file mode 100644 index 00000000000..7003d073d77 --- /dev/null +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleTraitMatcher.java @@ -0,0 +1,37 @@ +/* + * 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.gradle.trait; + +import org.openrewrite.Cursor; +import org.openrewrite.SourceFile; +import org.openrewrite.gradle.marker.GradleProject; +import org.openrewrite.internal.lang.Nullable; +import org.openrewrite.trait.SimpleTraitMatcher; +import org.openrewrite.trait.Trait; + +import java.util.Optional; + +public abstract class GradleTraitMatcher> extends SimpleTraitMatcher { + protected @Nullable GradleProject getGradleProject(Cursor cursor) { + SourceFile sourceFile = cursor.firstEnclosing(SourceFile.class); + if (sourceFile == null) { + return null; + } + + Optional maybeGp = sourceFile.getMarkers().findFirst(GradleProject.class); + return maybeGp.orElse(null); + } +} diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/package-info.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/package-info.java new file mode 100644 index 00000000000..75cfc2dcf76 --- /dev/null +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +@NonNullApi +@NonNullFields +package org.openrewrite.gradle.trait; + +import org.openrewrite.internal.lang.NonNullApi; +import org.openrewrite.internal.lang.NonNullFields; diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/trait/GradleDependencyTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/trait/GradleDependencyTest.java new file mode 100644 index 00000000000..43b863c2dd9 --- /dev/null +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/trait/GradleDependencyTest.java @@ -0,0 +1,207 @@ +/* + * 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.gradle.trait; + +import org.junit.jupiter.api.Test; +import org.openrewrite.marker.SearchResult; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.gradle.Assertions.buildGradle; +import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi; +import static org.openrewrite.gradle.trait.Traits.gradleDependency; + +class GradleDependencyTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec + .beforeRecipe(withToolingApi()) + .recipe(RewriteTest.toRecipe(() -> gradleDependency().asVisitor(dep -> + SearchResult.found(dep.getTree(), dep.getResolvedDependency().getGav().toString())))); + } + + @Test + void literal() { + rewriteRun( + buildGradle( + """ + plugins { + id "java" + } + + repositories { + mavenCentral() + } + + dependencies { + implementation "com.google.guava:guava:28.2-jre" + } + """, + """ + plugins { + id "java" + } + + repositories { + mavenCentral() + } + + dependencies { + /*~~(com.google.guava:guava:28.2-jre)~~>*/implementation "com.google.guava:guava:28.2-jre" + } + """ + ) + ); + } + + @Test + void groovyString() { + rewriteRun( + buildGradle( + """ + plugins { + id "java" + } + + repositories { + mavenCentral() + } + + dependencies { + def version = "28.2-jre" + implementation "com.google.guava:guava:${version}" + } + """, + """ + plugins { + id "java" + } + + repositories { + mavenCentral() + } + + dependencies { + def version = "28.2-jre" + /*~~(com.google.guava:guava:28.2-jre)~~>*/implementation "com.google.guava:guava:${version}" + } + """ + ) + ); + } + + @Test + void groovyMapEntry() { + rewriteRun( + buildGradle( + """ + plugins { + id "java" + } + + repositories { + mavenCentral() + } + + dependencies { + implementation group: "com.google.guava", name: "guava", version: "28.2-jre" + } + """, + """ + plugins { + id "java" + } + + repositories { + mavenCentral() + } + + dependencies { + /*~~(com.google.guava:guava:28.2-jre)~~>*/implementation group: "com.google.guava", name: "guava", version: "28.2-jre" + } + """ + ) + ); + } + + @Test + void platform() { + rewriteRun( + buildGradle( + """ + plugins { + id "java" + } + + repositories { + mavenCentral() + } + + dependencies { + implementation(platform("com.google.guava:guava:28.2-jre")) + } + """, + """ + plugins { + id "java" + } + + repositories { + mavenCentral() + } + + dependencies { + /*~~(com.google.guava:guava:28.2-jre)~~>*/implementation(platform("com.google.guava:guava:28.2-jre")) + } + """ + ) + ); + } + + @Test + void enforcedPlatform() { + rewriteRun( + buildGradle( + """ + plugins { + id "java" + } + + repositories { + mavenCentral() + } + + dependencies { + implementation(enforcedPlatform("com.google.guava:guava:28.2-jre")) + } + """, + """ + plugins { + id "java" + } + + repositories { + mavenCentral() + } + + dependencies { + /*~~(com.google.guava:guava:28.2-jre)~~>*/implementation(enforcedPlatform("com.google.guava:guava:28.2-jre")) + } + """ + ) + ); + } +} diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/trait/Traits.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/trait/Traits.java new file mode 100644 index 00000000000..356c2d2dd68 --- /dev/null +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/trait/Traits.java @@ -0,0 +1,25 @@ +/* + * 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.gradle.trait; + +public class Traits { + private Traits() { + } + + public static GradleDependency.Matcher gradleDependency() { + return new GradleDependency.Matcher(); + } +}