Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new recipe to add comments to a Java method #4719

Merged
merged 8 commits into from
Nov 26, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright 2021 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.java;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Option;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.java.search.DeclaresMethod;
import org.openrewrite.java.tree.Comment;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.TextComment;
import org.openrewrite.marker.Markers;

import java.util.List;

/**
* This recipe adds a comment to a method in a Java source file. The comment can be a single line or a multiline comment.
* <p>
* The {@link AddCommentToMethod#comment} must be supplied and is the comment to add.
* <p>
* The {@link AddCommentToMethod#methodPattern} is a pattern to match methods to add the comment to.
* <p>
* The {@link AddCommentToMethod#isMultiline} is an optional flag (defaulted to false) to indicate if the comment is a multiline comment.
*/
@Value
@EqualsAndHashCode(callSuper = false)
public class AddCommentToMethod extends Recipe {
@Override
public String getDisplayName() {
return "Add comment";
}

@Override
public String getDescription() {
return "Add a comment to a Java source file.";
}

@Option(displayName = "Comment",
description = "The comment to add.",
example = "This is a comment.")
String comment;

@Option(displayName = "Method pattern",
description = "A pattern to match methods to add the comment to.",
example = "java.util.List add*(..)")
String methodPattern;

@Option(displayName = "Multiline",
description = "Comments use by default single line // but they can use multiline /* */.",
required = false)
@Nullable
Boolean isMultiline;

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
MethodMatcher methodMatcher = new MethodMatcher(methodPattern);
return Preconditions.check(
new DeclaresMethod<>(methodMatcher),
new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
J.MethodDeclaration md = super.visitMethodDeclaration(method, ctx);
J.ClassDeclaration cd = getCursor().firstEnclosingOrThrow(J.ClassDeclaration.class);
List<Comment> comments = md.getComments();
if (methodMatcher.matches(md, cd) && comments.stream()
.filter(TextComment.class::isInstance)
.map(TextComment.class::cast)
.noneMatch(c -> comment.equals(c.getText()))) {

TextComment textComment = new TextComment(Boolean.TRUE.equals(isMultiline), comment, md.getPrefix().getWhitespace(), Markers.EMPTY);
return md.withComments(ListUtils.concat(comments, textComment));
}
return md;
}
}
);
}
}
agoncal marked this conversation as resolved.
Show resolved Hide resolved
timtebeek marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* Copyright 2024 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.java;
agoncal marked this conversation as resolved.
Show resolved Hide resolved

import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;

class AddCommentToMethodTest implements RewriteTest {

private static final String SHORT_COMMENT = " Short comment to add";
private static final String LONG_COMMENT = " This is a very long comment to add. The comment uses multiline comments, not single line.";

@DocumentExample
@Test
void addSingleLineComment() {
rewriteRun(
spec -> spec.recipe(new AddCommentToMethod(SHORT_COMMENT, "foo.Foo bar(..)", false)),
//language=java
java(
"""
package foo;
public class Foo {
public void bar(String arg) {}
}
""",
"""
package foo;
public class Foo {
// Short comment to add
public void bar(String arg) {}
}
"""
)
);
}

@Test
void addLongComment() {
rewriteRun(
spec -> spec.recipe(new AddCommentToMethod(LONG_COMMENT, "foo.Foo bar(..)", true)),
//language=java
java(
"""
package foo;
public class Foo {
public void bar(String arg) {}
}
""",
"""
package foo;
public class Foo {
/* This is a very long comment to add. The comment uses multiline comments, not single line.*/
public void bar(String arg) {}
}
"""
)
);
}

@Test
void addSingleLineCommentToExistingSingleLineComments() {
rewriteRun(
spec -> spec.recipe(new AddCommentToMethod(SHORT_COMMENT, "foo.Foo bar(..)", false)),
//language=java
java(
"""
package foo;
public class Foo {
// Existing single line comment
// Another existing single line comment
public void bar(String arg) {}
}
""",
"""
package foo;
public class Foo {
// Existing single line comment
// Another existing single line comment
// Short comment to add
public void bar(String arg) {}
}
"""
)
);
}

@Test
void addSingleLineCommentToExistingMultiLineComment() {
rewriteRun(
spec -> spec.recipe(new AddCommentToMethod(SHORT_COMMENT, "foo.Foo bar(..)", false)),
//language=java
java(
"""
package foo;
public class Foo {
/**
* Existing multi line
* comment
*/
public void bar(String arg) {}
}
""",
"""
package foo;
public class Foo {
/**
* Existing multi line
* comment
*/
// Short comment to add
public void bar(String arg) {}
}
"""
)
);
}

@Test
void addLongCommentToExistingMultiLineComment() {
rewriteRun(
spec -> spec.recipe(new AddCommentToMethod(LONG_COMMENT, "foo.Foo bar(..)", true)),
//language=java
java(
"""
package foo;
public class Foo {
/**
* Existing multi line
* comment
*/
public void bar(String arg) {}
}
""",
"""
package foo;
public class Foo {
/**
* Existing multi line
* comment
*/
/* This is a very long comment to add. The comment uses multiline comments, not single line.*/
public void bar(String arg) {}
}
"""
)
);
}
}
agoncal marked this conversation as resolved.
Show resolved Hide resolved
timtebeek marked this conversation as resolved.
Show resolved Hide resolved