Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Option;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.jspecify.annotations.Nullable;
import org.openrewrite.*;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.maven.trait.MavenPlugin;
import org.openrewrite.maven.tree.MavenResolutionResult;
Expand All @@ -30,6 +28,7 @@
import org.openrewrite.xml.tree.Xml;

import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

@Value
Expand Down Expand Up @@ -57,67 +56,96 @@ public class AddAnnotationProcessor extends Recipe {
String displayName = "Add an annotation processor to `maven-compiler-plugin`";

String description = "Add an annotation processor to the maven compiler plugin. Will not do anything if it already exists. " +
"Also doesn't add anything when no other annotation processors are defined yet. " +
"(Perhaps `ChangePluginConfiguration` can be used).";
"Also doesn't add anything when no other annotation processors are defined yet. " +
"(Perhaps `ChangePluginConfiguration` can be used).";

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new MavenVisitor<ExecutionContext>() {

return new TreeVisitor<Tree, ExecutionContext>() {
@Override
public Xml visitTag(Xml.Tag tag, ExecutionContext ctx) {
Xml.Tag plugins = (Xml.Tag) super.visitTag(tag, ctx);
plugins = (Xml.Tag) new MavenPlugin.Matcher().asVisitor(plugin -> {
if (MAVEN_COMPILER_PLUGIN_GROUP_ID.equals(plugin.getGroupId()) &&
MAVEN_COMPILER_PLUGIN_ARTIFACT_ID.equals(plugin.getArtifactId())) {
MavenResolutionResult mrr = getResolutionResult();
AtomicReference<TreeVisitor<?, ExecutionContext>> afterVisitor = new AtomicReference<>();
Xml.Tag modifiedPlugin = new XmlIsoVisitor<ExecutionContext>() {
@Override
public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) {
Xml.Tag tg = super.visitTag(tag, ctx);
if ("annotationProcessorPaths".equals(tg.getName())) {
public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx, Cursor parent) {
if (tree == null) {
return tree;
}

// if we find more than one project pom, it has to be multi-module
boolean isMultiModule = tree.getMarkers()
.findFirst(MavenResolutionResult.class)
.map(MavenResolutionResult::getProjectPoms)
.map(Map::size)
.map(size -> size > 1)
.orElseThrow(() -> new IllegalStateException("Unable to determine the number of project poms"));

// maybe add the plugin to //build/pluginManagement/plugins or //build/plugins is not present yet
tree = new AddPluginVisitor(isMultiModule, MAVEN_COMPILER_PLUGIN_GROUP_ID, MAVEN_COMPILER_PLUGIN_ARTIFACT_ID, null, "<configuration><annotationProcessorPaths/></configuration>", null, null, null).visit(tree, ctx);

// configure the plugin adding process
return new MavenIsoVisitor<ExecutionContext>() {
@Override
public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) {
Xml.Tag plugins = super.visitTag(tag, ctx);
plugins = (Xml.Tag) new MavenPlugin.Matcher(isMultiModule, MAVEN_COMPILER_PLUGIN_GROUP_ID, MAVEN_COMPILER_PLUGIN_ARTIFACT_ID).asVisitor(plugin -> {

MavenResolutionResult mrr = getResolutionResult();
AtomicReference<TreeVisitor<?, ExecutionContext>> maybePropertyUpdate = new AtomicReference<>();
Xml.Tag modifiedPlugin = new XmlIsoVisitor<ExecutionContext>() {
@Override
public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) {
Xml.Tag tg = super.visitTag(tag, ctx);

if (!"annotationProcessorPaths".equals(tg.getName())) {
return tg;
}

// iterate the children (annotation processor paths) and try to update the version
for (int i = 0; i < tg.getChildren().size(); i++) {
Xml.Tag child = tg.getChildren().get(i);
if (groupId.equals(child.getChildValue("groupId").orElse(null)) &&
artifactId.equals(child.getChildValue("artifactId").orElse(null))) {
if (!version.equals(child.getChildValue("version").orElse(null))) {
String oldVersion = child.getChildValue("version").orElse("");
boolean oldVersionUsesProperty = oldVersion.startsWith("${");
String lookupVersion = oldVersionUsesProperty ?
mrr.getPom().getValue(oldVersion.trim()) :
oldVersion;
VersionComparator comparator = Semver.validate(lookupVersion, null).getValue();
if (comparator.compare(version, lookupVersion) > 0) {
if (oldVersionUsesProperty) {
afterVisitor.set(new ChangePropertyValue(oldVersion, version, null, null).getVisitor());
} else {
List<Xml.Tag> tags = tg.getChildren();
tags.set(i, child.withChildValue("version", version));
return tg.withContent(tags);
}
if (!groupId.equals(child.getChildValue("groupId").orElse(null)) ||
!artifactId.equals(child.getChildValue("artifactId").orElse(null))) {
continue;
}

if (!version.equals(child.getChildValue("version").orElse(null))) {
String oldVersion = child.getChildValue("version").orElse("");
boolean oldVersionUsesProperty = oldVersion.startsWith("${");
String lookupVersion = oldVersionUsesProperty ? mrr.getPom().getValue(oldVersion.trim()) : oldVersion;
VersionComparator comparator = Semver.validate(lookupVersion, null).getValue();
if (comparator.compare(version, lookupVersion) > 0) {
if (oldVersionUsesProperty) {
// a maven property is used here, update in properties section later
maybePropertyUpdate.set(new ChangePropertyValue(oldVersion, version, null, null).getVisitor());
} else {
// update the paths version directly
List<Xml.Tag> tags = tg.getChildren();
tags.set(i, child.withChildValue("version", version));
return tg.withContent(tags);
}
}
return tg;
}

return tg;
}

// not found so we add it
return tg.withContent(ListUtils.concat(tg.getChildren(), Xml.Tag.build(String.format(
"<path>\n<groupId>%s</groupId>\n<artifactId>%s</artifactId>\n<version>%s</version>\n</path>",
groupId, artifactId, version))));
}
return tg;
}.visitTag(plugin.getTree(), ctx);

if (maybePropertyUpdate.get() != null) {
doAfterVisit(maybePropertyUpdate.get());
}
}.visitTag(plugin.getTree(), ctx);
if (afterVisitor.get() != null) {
doAfterVisit(afterVisitor.get());

return modifiedPlugin;
}).visitNonNull(plugins, 0);
if (plugins != tag) {
plugins = autoFormat(plugins, ctx);
}
return modifiedPlugin;
return plugins;
}
return plugin.getTree();
}).visitNonNull(plugins, 0);
if (plugins != tag) {
plugins = autoFormat(plugins, ctx);
}
return plugins;
}.visit(tree, ctx);
}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright 2020 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.maven;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.intellij.lang.annotations.Language;
import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Option;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.xml.XPathMatcher;

@Value
@EqualsAndHashCode(callSuper = false)
public class AddManagedPlugin extends Recipe {
@Option(displayName = "Group",
description = "The first part of a dependency coordinate 'org.openrewrite.maven:rewrite-maven-plugin:VERSION'.",
example = "org.openrewrite.maven")
String groupId;

@Option(displayName = "Artifact",
description = "The second part of a dependency coordinate 'org.openrewrite.maven:rewrite-maven-plugin:VERSION'.",
example = "rewrite-maven-plugin")
String artifactId;

@Option(displayName = "Version",
description = "A fixed version of the plugin to add.",
example = "1.0.0",
required = false)
@Nullable
String version;

@Language("xml")
@Option(displayName = "Configuration",
description = "Optional plugin configuration provided as raw XML",
example = "<configuration><foo>foo</foo></configuration>",
required = false)
@Nullable
String configuration;

@Language("xml")
@Option(displayName = "Dependencies",
description = "Optional plugin dependencies provided as raw XML.",
example = "<dependencies><dependency><groupId>com.yourorg</groupId><artifactId>core-lib</artifactId><version>1.0.0</version></dependency></dependencies>",
required = false)
@Nullable
String dependencies;

@Language("xml")
@Option(displayName = "Executions",
description = "Optional executions provided as raw XML.",
example = "<executions><execution><phase>generate-sources</phase><goals><goal>add-source</goal></goals></execution></executions>",
required = false)
@Nullable
String executions;

@Option(displayName = "File pattern",
description = "A glob expression that can be used to constrain which directories or source files should be searched. " +
"Multiple patterns may be specified, separated by a semicolon `;`. " +
"If multiple patterns are supplied any of the patterns matching will be interpreted as a match. " +
"When not set, all source files are searched. ",
required = false,
example = "**/*-parent/grpc-*/pom.xml")
@Nullable
String filePattern;

String displayName = "Add Managed Maven plugin";

@Override
public String getInstanceNameSuffix() {
return String.format("`%s:%s:%s`", groupId, artifactId, version);
}

String description = "Add the specified Maven plugin to the Plugin Managed of the pom.xml.";

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new AddPluginVisitor(true, groupId, artifactId, version, configuration, dependencies, executions, filePattern);
}
}
81 changes: 3 additions & 78 deletions rewrite-maven/src/main/java/org/openrewrite/maven/AddPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@
@Value
@EqualsAndHashCode(callSuper = false)
public class AddPlugin extends Recipe {

private static final XPathMatcher BUILD_MATCHER = new XPathMatcher("/project/build");

@Option(displayName = "Group",
description = "The first part of a dependency coordinate 'org.openrewrite.maven:rewrite-maven-plugin:VERSION'.",
example = "org.openrewrite.maven")
Expand All @@ -59,13 +56,15 @@ public class AddPlugin extends Recipe {
@Nullable
String configuration;

@Language("xml")
@Option(displayName = "Dependencies",
description = "Optional plugin dependencies provided as raw XML.",
example = "<dependencies><dependency><groupId>com.yourorg</groupId><artifactId>core-lib</artifactId><version>1.0.0</version></dependency></dependencies>",
required = false)
@Nullable
String dependencies;

@Language("xml")
@Option(displayName = "Executions",
description = "Optional executions provided as raw XML.",
example = "<executions><execution><phase>generate-sources</phase><goals><goal>add-source</goal></goals></execution></executions>",
Expand Down Expand Up @@ -94,80 +93,6 @@ public String getInstanceNameSuffix() {

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new AddPluginVisitor();
}

private class AddPluginVisitor extends MavenIsoVisitor<ExecutionContext> {

@Override
public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) {
if (filePattern != null) {
return PathUtils.matchesGlob(sourceFile.getSourcePath(), filePattern) && super.isAcceptable(sourceFile, ctx);
}

MavenResolutionResult mrr = sourceFile.getMarkers().findFirst(MavenResolutionResult.class).orElse(null);
if (mrr == null || mrr.parentPomIsProjectPom()) {
return false;
}

return super.isAcceptable(sourceFile, ctx);
}

@Override
public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) {
Xml.Tag root = document.getRoot();
if (!root.getChild("build").isPresent()) {
document = (Xml.Document) new AddToTagVisitor<>(root, Xml.Tag.build("<build/>"))
.visitNonNull(document, ctx, getCursor().getParentOrThrow());
}
return super.visitDocument(document, ctx);
}

@Override
public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) {
Xml.Tag t = super.visitTag(tag, ctx);

if (BUILD_MATCHER.matches(getCursor())) {
Optional<Xml.Tag> maybePlugins = t.getChild("plugins");
Xml.Tag plugins;
if (maybePlugins.isPresent()) {
plugins = maybePlugins.get();
} else {
t = (Xml.Tag) new AddToTagVisitor<>(t, Xml.Tag.build("<plugins/>")).visitNonNull(t, ctx, getCursor().getParentOrThrow());
//noinspection OptionalGetWithoutIsPresent
plugins = t.getChild("plugins").get();
}

Optional<Xml.Tag> maybePlugin = plugins.getChildren().stream()
.filter(plugin ->
"plugin".equals(plugin.getName()) &&
groupId.equals(plugin.getChildValue("groupId").orElse(null)) &&
artifactId.equals(plugin.getChildValue("artifactId").orElse(null))
)
.findAny();

if (maybePlugin.isPresent()) {
Xml.Tag plugin = maybePlugin.get();
if (version != null && !version.equals(plugin.getChildValue("version").orElse(null))) {
if (plugin.getChild("version").isPresent()) {
t = (Xml.Tag) new ChangeTagValueVisitor<>(plugin.getChild("version").get(), version).visitNonNull(t, ctx, getCursor().getParentOrThrow());
}
}
} else {
Xml.Tag pluginTag = Xml.Tag.build(
"<plugin>\n" +
"<groupId>" + groupId + "</groupId>\n" +
"<artifactId>" + artifactId + "</artifactId>\n" +
(version != null ? "<version>" + version + "</version>\n" : "") +
(executions != null ? executions.trim() + "\n" : "") +
(configuration != null ? configuration.trim() + "\n" : "") +
(dependencies != null ? dependencies.trim() + "\n" : "") +
"</plugin>");
t = (Xml.Tag) new AddToTagVisitor<>(plugins, pluginTag).visitNonNull(t, ctx, getCursor().getParentOrThrow());
}
}

return t;
}
return new AddPluginVisitor(false, groupId, artifactId, version, configuration, dependencies, executions, filePattern);
}
}
Loading