TL;DR: How to preserve the Javadoc, line- and block-comment when creating a new java type based on a given type?
Long:
I am batch converting an infinite amount of types (Java classes) based on an unwanted base class towards Java enum types by using a headless eclipse application using JDT. My approach is to create a new EnumDeclaration
based on the type information and adding the EnumConstantDeclaration
s based on the FieldDeclaration
s (exluding the serialVersionUID
) of the initial class type. After that I add the MethodDeclaration
s (excluding the constructor) by simply adding a clone of the original MethodDeclaration
s to the BodyDelcaration
of the newly created EnumDeclaration
. Once I have done that, which is quite straight forward thanks to the great API, I do the following...
// create the EnumDeclaration from the given UnwantedClass CompilationUnit
final EnumDeclaration enumTypeDeclaration = createEnumDeclaration(cu, astRoot, methodDeclarations, ast);
// Find the original UnwantedClass TypeDeclaration and replace it with the new EnumDeclaration
astRoot.accept(new ASTVisitor() {
@Override
public boolean visit(final TypeDeclaration node) {
rewriter.replace(node, enumTypeDeclaration, null);
return false;
}
});
...to replace the original Java class Type
with the new EnumDeclaration
. This works almost perfectly. The only thing missing are all the Line
-, Block
- and JavadocComment
elements of the original Java type. I found out that you can at least retrieve all Comment
instances by:
List<Comment> comments = cu.getCommentList();
if (comments != null) {
for (Comment comment : comments) {
comment.accept(visitor);
}
}
That gives me all the comment, but I haven't figured out how to map a Comment
instance to a BodyDeclaration
, because these Comment
instances are basically free floating all over the Source
file and are only linked by their startPosition within the Source
file.
There is the getAlternateRoot
method, but I haven't managed either to utilize that one.
The question is: How do I preserve the Comment
instances from the original type and put them at the correct position in the new type?
It seems, there is no straight forward approach to solve this, because javadoc (or just comments in general) in java files have no direct relation to the actual code they are meant to comment. Its just common sense among developers that we are usually put the comment above the code we are commenting. So I took the following path to solve this:
The adding of the comment isn't as straight forward as I thought, because in newer JSL you cant simply set the comment String in javadoc (only supported in JSL2, but in this version we hadn't enums). So, I used the following code:
private void setJavadoc(final BodyDeclaration bodyDeclaration, final CommentEntry commentEntry) {
final Javadoc javadoc = (Javadoc) ASTNode.copySubtree(ast, commentEntry.getComment());
final TagElement tagElement = ast.newTagElement();
final TextElement textElement = ast.newTextElement();
textElement.setText(commentEntry.getText());
tagElement.fragments().add(textElement);
javadoc.tags().add(tagElement);
bodyDeclaration.setJavadoc(javadoc);
}
As you can see, the actual comment text can be simply added by using a TextElement, even though it contains Tags. To make it perfect you may need to substring the comment text previously extracted from the source file, because it will contain the /** and */.