JavaParser Source Viewer

Home|JavaParser/com/github/javaparser/CommentsInserter.java
1/*
2 * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
3 * Copyright (C) 2011, 2013-2020 The JavaParser Team.
4 *
5 * This file is part of JavaParser.
6 *
7 * JavaParser can be used either under the terms of
8 * a) the GNU Lesser General Public License as published by
9 *     the Free Software Foundation, either version 3 of the License, or
10 *     (at your option) any later version.
11 * b) the terms of the Apache License
12 *
13 * You should have received a copy of both licenses in LICENCE.LGPL and
14 * LICENCE.APACHE. Please refer to those files for details.
15 *
16 * JavaParser is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU Lesser General Public License for more details.
20 */
21
22package com.github.javaparser;
23
24import com.github.javaparser.ast.CompilationUnit;
25import com.github.javaparser.ast.Modifier;
26import com.github.javaparser.ast.Node;
27import com.github.javaparser.ast.comments.Comment;
28import com.github.javaparser.ast.comments.LineComment;
29import com.github.javaparser.utils.PositionUtils;
30
31import java.util.Collections;
32import java.util.LinkedList;
33import java.util.List;
34import java.util.TreeSet;
35
36import static com.github.javaparser.ast.Node.NODE_BY_BEGIN_POSITION;
37import static java.util.stream.Collectors.toList;
38
39/**
40 * Assigns comments to nodes of the AST.
41 *
42 * @author Sebastian Kuerten
43 * @author Júlio Vilmar Gesser
44 */
45class CommentsInserter {
46    private final ParserConfiguration configuration;
47
48    CommentsInserter(ParserConfiguration configuration) {
49        this.configuration = configuration;
50    }
51
52    /**
53     * Comments are attributed to the thing they comment and are removed from
54     * the comments.
55     */
56    private void insertComments(CompilationUnit cuTreeSet<Commentcomments) {
57        if (comments.isEmpty())
58            return;
59
60        /* I should sort all the direct children and the comments, if a comment
61         is the first thing then it is a comment to the CompilationUnit */
62
63        // FIXME if there is no package it could be also a comment to the following class...
64        // so I could use some heuristics in these cases to distinguish the two
65        // cases
66
67        List<Nodechildren = cu.getChildNodes();
68
69        Comment firstComment = comments.iterator().next();
70        if (cu.getPackageDeclaration().isPresent()
71                && (children.isEmpty() || PositionUtils.areInOrder(
72                firstCommentcu.getPackageDeclaration().get()))) {
73            cu.setComment(firstComment);
74            comments.remove(firstComment);
75        }
76    }
77
78    /**
79     * This method try to attributes the nodes received to child of the node. It
80     * returns the node that were not attributed.
81     */
82    void insertComments(Node nodeTreeSet<CommentcommentsToAttribute) {
83        if (commentsToAttribute.isEmpty())
84            return;
85
86        if (node instanceof CompilationUnit) {
87            insertComments((CompilationUnitnodecommentsToAttribute);
88        }
89
90        /* the comment can...
91         1) be inside one of the children, then the comment should be associated to this child
92         2) be outside all children. They could be preceding nothing, a comment or a child.
93            If they preceed a child they are assigned to it, otherwise they remain "orphans"
94         */
95
96        List<Nodechildren = node.getChildNodes().stream()
97                // Never attribute comments to modifiers.
98                .filter(n -> !(n instanceof Modifier))
99                .collect(toList());
100
101        boolean attributeToAnnotation = !(configuration.isIgnoreAnnotationsWhenAttributingComments());
102        for (Node child : children) {
103            TreeSet<CommentcommentsInsideChild = new TreeSet<>(NODE_BY_BEGIN_POSITION);
104            commentsInsideChild.addAll(
105                    commentsToAttribute.stream()
106                            .filter(comment -> comment.getRange().isPresent())
107                            .filter(comment -> PositionUtils.nodeContains(childcomment, !attributeToAnnotation))
108                            .collect(toList())
109            );
110            commentsToAttribute.removeAll(commentsInsideChild);
111            insertComments(childcommentsInsideChild);
112        }
113
114        attributeLineCommentsOnSameLine(commentsToAttributechildren);
115
116        /* if a comment is on the line right before a node it should belong
117        to that node*/
118        if (!commentsToAttribute.isEmpty()) {
119            if (commentIsOnNextLine(nodecommentsToAttribute.first())) {
120                node.setComment(commentsToAttribute.first());
121                commentsToAttribute.remove(commentsToAttribute.first());
122            }
123        }
124
125        /* at this point I create an ordered list of all remaining comments and
126         children */
127        Comment previousComment = null;
128        final List<CommentattributedComments = new LinkedList<>();
129        List<NodechildrenAndComments = new LinkedList<>();
130        // Avoid attributing comments to a meaningless container.
131        childrenAndComments.addAll(children);
132        commentsToAttribute.removeAll(attributedComments);
133
134        childrenAndComments.addAll(commentsToAttribute);
135        PositionUtils.sortByBeginPosition(childrenAndComments,
136                configuration.isIgnoreAnnotationsWhenAttributingComments());
137
138        for (Node thing : childrenAndComments) {
139            if (thing instanceof Comment) {
140                previousComment = (Commentthing;
141                if (!previousComment.isOrphan()) {
142                    previousComment = null;
143                }
144            } else {
145                if (previousComment != null && !thing.getComment().isPresent()) {
146                    if (!configuration.isDoNotAssignCommentsPrecedingEmptyLines()
147                            || !thereAreLinesBetween(previousCommentthing)) {
148                        thing.setComment(previousComment);
149                        attributedComments.add(previousComment);
150                        previousComment = null;
151                    }
152                }
153            }
154        }
155
156        commentsToAttribute.removeAll(attributedComments);
157
158        // all the remaining are orphan nodes
159        for (Comment c : commentsToAttribute) {
160            if (c.isOrphan()) {
161                node.addOrphanComment(c);
162            }
163        }
164    }
165
166    private void attributeLineCommentsOnSameLine(TreeSet<CommentcommentsToAttributeList<Nodechildren) {
167        /* I can attribute in line comments to elements preceeding them, if
168         there is something contained in their line */
169        List<CommentattributedComments = new LinkedList<>();
170        commentsToAttribute.stream()
171                .filter(comment -> comment.getRange().isPresent())
172                .filter(Comment::isLineComment)
173                .forEach(comment -> children.stream()
174                        .filter(child -> child.getRange().isPresent())
175                        .forEach(child -> {
176                            Range commentRange = comment.getRange().get();
177                            Range childRange = child.getRange().get();
178                            if (childRange.end.line == commentRange.begin.line
179                                    && attributeLineCommentToNodeOrChild(child,
180                                    comment.asLineComment())) {
181                                attributedComments.add(comment);
182                            }
183                        }));
184        commentsToAttribute.removeAll(attributedComments);
185    }
186
187    private boolean attributeLineCommentToNodeOrChild(Node nodeLineComment lineComment) {
188        if (!node.getRange().isPresent() || !lineComment.getRange().isPresent()) {
189            return false;
190        }
191
192        // The node start and end at the same line as the comment,
193        // let's give to it the comment
194        if (node.getBegin().get().line == lineComment.getBegin().get().line
195                && !node.getComment().isPresent()) {
196            if (!(node instanceof Comment)) {
197                node.setComment(lineComment);
198            }
199            return true;
200        } else {
201            // try with all the children, sorted by reverse position (so the
202            // first one is the nearest to the comment
203            List<Nodechildren = new LinkedList<>();
204            children.addAll(node.getChildNodes());
205            PositionUtils.sortByBeginPosition(children);
206            Collections.reverse(children);
207
208            for (Node child : children) {
209                if (attributeLineCommentToNodeOrChild(childlineComment)) {
210                    return true;
211                }
212            }
213
214            return false;
215        }
216    }
217
218    private boolean thereAreLinesBetween(Node aNode b) {
219        if (!a.getRange().isPresent() || !b.getRange().isPresent()) {
220            return true;
221        }
222        if (!PositionUtils.areInOrder(ab)) {
223            return thereAreLinesBetween(ba);
224        }
225        int endOfA = a.getEnd().get().line;
226        return b.getBegin().get().line > endOfA + 1;
227    }
228
229    private boolean commentIsOnNextLine(Node aComment c) {
230        if (!c.getRange().isPresent() || !a.getRange().isPresent()) return false;
231        return c.getRange().get().end.line + 1 == a.getRange().get().begin.line;
232    }
233
234}
235
MembersX
CommentsInserter:attributeLineCommentsOnSameLine:Block:Block:childRange
CommentsInserter:insertComments:Block:children
CommentsInserter:insertComments:Block:firstComment
CommentsInserter:attributeLineCommentsOnSameLine:Block:attributedComments
CommentsInserter:CommentsInserter
CommentsInserter:attributeLineCommentsOnSameLine
CommentsInserter:attributeLineCommentToNodeOrChild:Block:Block:children
CommentsInserter:insertComments:Block:Block:commentsInsideChild
CommentsInserter:insertComments:Block:previousComment
CommentsInserter:attributeLineCommentsOnSameLine:Block:Block:commentRange
CommentsInserter:insertComments
CommentsInserter:insertComments:Block:attributedComments
CommentsInserter:insertComments:Block:attributeToAnnotation
CommentsInserter:thereAreLinesBetween
CommentsInserter:configuration
CommentsInserter:attributeLineCommentToNodeOrChild
CommentsInserter:thereAreLinesBetween:Block:endOfA
CommentsInserter:insertComments:Block:childrenAndComments
CommentsInserter:commentIsOnNextLine
Members
X