|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package com.github.javaparser; |
|
|
|
import com.github.javaparser.ast.CompilationUnit; |
|
import com.github.javaparser.ast.Modifier; |
|
import com.github.javaparser.ast.Node; |
|
import com.github.javaparser.ast.comments.Comment; |
|
import com.github.javaparser.ast.comments.LineComment; |
|
import com.github.javaparser.utils.PositionUtils; |
|
|
|
import java.util.Collections; |
|
import java.util.LinkedList; |
|
import java.util.List; |
|
import java.util.TreeSet; |
|
|
|
import static com.github.javaparser.ast.Node.NODE_BY_BEGIN_POSITION; |
|
import static java.util.stream.Collectors.toList; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
class CommentsInserter { |
|
private final ParserConfiguration configuration; |
|
|
|
CommentsInserter(ParserConfiguration configuration) { |
|
this.configuration = configuration; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void insertComments(CompilationUnit cu, TreeSet<Comment> comments) { |
|
if (comments.isEmpty()) |
|
return; |
|
|
|
/* I should sort all the direct children and the comments, if a comment |
|
is the first thing then it is a comment to the CompilationUnit */ |
|
|
|
// FIXME if there is no package it could be also a comment to the following class... |
|
// so I could use some heuristics in these cases to distinguish the two |
|
// cases |
|
|
|
List<Node> children = cu.getChildNodes(); |
|
|
|
Comment firstComment = comments.iterator().next(); |
|
if (cu.getPackageDeclaration().isPresent() |
|
&& (children.isEmpty() || PositionUtils.areInOrder( |
|
firstComment, cu.getPackageDeclaration().get()))) { |
|
cu.setComment(firstComment); |
|
comments.remove(firstComment); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
void insertComments(Node node, TreeSet<Comment> commentsToAttribute) { |
|
if (commentsToAttribute.isEmpty()) |
|
return; |
|
|
|
if (node instanceof CompilationUnit) { |
|
insertComments((CompilationUnit) node, commentsToAttribute); |
|
} |
|
|
|
/* the comment can... |
|
1) be inside one of the children, then the comment should be associated to this child |
|
2) be outside all children. They could be preceding nothing, a comment or a child. |
|
If they preceed a child they are assigned to it, otherwise they remain "orphans" |
|
*/ |
|
|
|
List<Node> children = node.getChildNodes().stream() |
|
|
|
.filter(n -> !(n instanceof Modifier)) |
|
.collect(toList()); |
|
|
|
boolean attributeToAnnotation = !(configuration.isIgnoreAnnotationsWhenAttributingComments()); |
|
for (Node child : children) { |
|
TreeSet<Comment> commentsInsideChild = new TreeSet<>(NODE_BY_BEGIN_POSITION); |
|
commentsInsideChild.addAll( |
|
commentsToAttribute.stream() |
|
.filter(comment -> comment.getRange().isPresent()) |
|
.filter(comment -> PositionUtils.nodeContains(child, comment, !attributeToAnnotation)) |
|
.collect(toList()) |
|
); |
|
commentsToAttribute.removeAll(commentsInsideChild); |
|
insertComments(child, commentsInsideChild); |
|
} |
|
|
|
attributeLineCommentsOnSameLine(commentsToAttribute, children); |
|
|
|
|
|
to that node*/ |
|
if (!commentsToAttribute.isEmpty()) { |
|
if (commentIsOnNextLine(node, commentsToAttribute.first())) { |
|
node.setComment(commentsToAttribute.first()); |
|
commentsToAttribute.remove(commentsToAttribute.first()); |
|
} |
|
} |
|
|
|
|
|
children */ |
|
Comment previousComment = null; |
|
final List<Comment> attributedComments = new LinkedList<>(); |
|
List<Node> childrenAndComments = new LinkedList<>(); |
|
|
|
childrenAndComments.addAll(children); |
|
commentsToAttribute.removeAll(attributedComments); |
|
|
|
childrenAndComments.addAll(commentsToAttribute); |
|
PositionUtils.sortByBeginPosition(childrenAndComments, |
|
configuration.isIgnoreAnnotationsWhenAttributingComments()); |
|
|
|
for (Node thing : childrenAndComments) { |
|
if (thing instanceof Comment) { |
|
previousComment = (Comment) thing; |
|
if (!previousComment.isOrphan()) { |
|
previousComment = null; |
|
} |
|
} else { |
|
if (previousComment != null && !thing.getComment().isPresent()) { |
|
if (!configuration.isDoNotAssignCommentsPrecedingEmptyLines() |
|
|| !thereAreLinesBetween(previousComment, thing)) { |
|
thing.setComment(previousComment); |
|
attributedComments.add(previousComment); |
|
previousComment = null; |
|
} |
|
} |
|
} |
|
} |
|
|
|
commentsToAttribute.removeAll(attributedComments); |
|
|
|
|
|
for (Comment c : commentsToAttribute) { |
|
if (c.isOrphan()) { |
|
node.addOrphanComment(c); |
|
} |
|
} |
|
} |
|
|
|
private void attributeLineCommentsOnSameLine(TreeSet<Comment> commentsToAttribute, List<Node> children) { |
|
|
|
there is something contained in their line */ |
|
List<Comment> attributedComments = new LinkedList<>(); |
|
commentsToAttribute.stream() |
|
.filter(comment -> comment.getRange().isPresent()) |
|
.filter(Comment::isLineComment) |
|
.forEach(comment -> children.stream() |
|
.filter(child -> child.getRange().isPresent()) |
|
.forEach(child -> { |
|
Range commentRange = comment.getRange().get(); |
|
Range childRange = child.getRange().get(); |
|
if (childRange.end.line == commentRange.begin.line |
|
&& attributeLineCommentToNodeOrChild(child, |
|
comment.asLineComment())) { |
|
attributedComments.add(comment); |
|
} |
|
})); |
|
commentsToAttribute.removeAll(attributedComments); |
|
} |
|
|
|
private boolean attributeLineCommentToNodeOrChild(Node node, LineComment lineComment) { |
|
if (!node.getRange().isPresent() || !lineComment.getRange().isPresent()) { |
|
return false; |
|
} |
|
|
|
// The node start and end at the same line as the comment, |
|
|
|
if (node.getBegin().get().line == lineComment.getBegin().get().line |
|
&& !node.getComment().isPresent()) { |
|
if (!(node instanceof Comment)) { |
|
node.setComment(lineComment); |
|
} |
|
return true; |
|
} else { |
|
// try with all the children, sorted by reverse position (so the |
|
|
|
List<Node> children = new LinkedList<>(); |
|
children.addAll(node.getChildNodes()); |
|
PositionUtils.sortByBeginPosition(children); |
|
Collections.reverse(children); |
|
|
|
for (Node child : children) { |
|
if (attributeLineCommentToNodeOrChild(child, lineComment)) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
} |
|
|
|
private boolean thereAreLinesBetween(Node a, Node b) { |
|
if (!a.getRange().isPresent() || !b.getRange().isPresent()) { |
|
return true; |
|
} |
|
if (!PositionUtils.areInOrder(a, b)) { |
|
return thereAreLinesBetween(b, a); |
|
} |
|
int endOfA = a.getEnd().get().line; |
|
return b.getBegin().get().line > endOfA + 1; |
|
} |
|
|
|
private boolean commentIsOnNextLine(Node a, Comment c) { |
|
if (!c.getRange().isPresent() || !a.getRange().isPresent()) return false; |
|
return c.getRange().get().end.line + 1 == a.getRange().get().begin.line; |
|
} |
|
|
|
} |