aboutsummaryrefslogtreecommitdiffstats
path: root/javaparser-core/src/main/java/com/github/javaparser/CommentsInserter.java
diff options
context:
space:
mode:
Diffstat (limited to 'javaparser-core/src/main/java/com/github/javaparser/CommentsInserter.java')
-rw-r--r--javaparser-core/src/main/java/com/github/javaparser/CommentsInserter.java217
1 files changed, 217 insertions, 0 deletions
diff --git a/javaparser-core/src/main/java/com/github/javaparser/CommentsInserter.java b/javaparser-core/src/main/java/com/github/javaparser/CommentsInserter.java
new file mode 100644
index 000000000..869883cbf
--- /dev/null
+++ b/javaparser-core/src/main/java/com/github/javaparser/CommentsInserter.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
+ * Copyright (C) 2011, 2013-2016 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser;
+
+import com.github.javaparser.ast.CompilationUnit;
+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 java.util.stream.Collectors;
+
+import static com.github.javaparser.ast.Node.NODE_BY_BEGIN_POSITION;
+
+/**
+ * Assigns comments to nodes of the AST.
+ *
+ * @author Sebastian Kuerten
+ * @author Júlio Vilmar Gesser
+ */
+class CommentsInserter {
+ private final ParserConfiguration configuration;
+
+ CommentsInserter(ParserConfiguration configuration) {
+ this.configuration = configuration;
+ }
+
+ /**
+ * Comments are attributed to the thing they comment and are removed from
+ * the comments.
+ */
+ 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
+ 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);
+ }
+ }
+
+ /**
+ * This method try to attributes the nodes received to child of the node. It
+ * returns the node that were not attributed.
+ */
+ void insertComments(Node node, TreeSet<Comment> commentsToAttribute) {
+ if (commentsToAttribute.isEmpty())
+ return;
+
+ if (node instanceof CompilationUnit) {
+ insertComments((CompilationUnit) node, commentsToAttribute);
+ }
+
+ // the comments can:
+ // 1) Inside one of the child, then it is the child that have to
+ // associate them
+ // 2) If they are not inside a child they could be preceeding nothing, a
+ // comment or a child
+ // if they preceed a child they are assigned to it, otherweise they
+ // remain "orphans"
+
+ List<Node> children = node.getChildNodes();
+
+ for (Node child : children) {
+ TreeSet<Comment> commentsInsideChild = new TreeSet<>(NODE_BY_BEGIN_POSITION);
+ commentsInsideChild.addAll(
+ commentsToAttribute.stream()
+ .filter(c -> c.getRange().isPresent())
+ .filter(c -> PositionUtils.nodeContains(child, c,
+ configuration.isDoNotConsiderAnnotationsAsNodeStartForCodeAttribution())).collect(Collectors.toList()));
+ commentsToAttribute.removeAll(commentsInsideChild);
+ insertComments(child, commentsInsideChild);
+ }
+
+ attributeLineCommentsOnSameLine(commentsToAttribute, children);
+
+ /* at this point I create an ordered list of all remaining comments and
+ children */
+ Comment previousComment = null;
+ final List<Comment> attributedComments = new LinkedList<>();
+ List<Node> childrenAndComments = new LinkedList<>();
+ // Avoid attributing comments to a meaningless container.
+ childrenAndComments.addAll(children);
+ commentsToAttribute.removeAll(attributedComments);
+
+ childrenAndComments.addAll(commentsToAttribute);
+ PositionUtils.sortByBeginPosition(childrenAndComments,
+ configuration.isDoNotConsiderAnnotationsAsNodeStartForCodeAttribution());
+
+ 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);
+
+ // all the remaining are orphan nodes
+ for (Comment c : commentsToAttribute) {
+ if (c.isOrphan()) {
+ node.addOrphanComment(c);
+ }
+ }
+ }
+
+ private void attributeLineCommentsOnSameLine(TreeSet<Comment> commentsToAttribute, List<Node> children) {
+ /* I can attribute in line comments to elements preceeding them, if
+ 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,
+ // let's give to it 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
+ // first one is the nearest to the comment
+ 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;
+ }
+
+}