View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.utils;
21  
22  import java.util.AbstractMap;
23  import java.util.Map;
24  
25  import org.antlr.v4.runtime.CommonToken;
26  
27  import com.puppycrawl.tools.checkstyle.DetailAstImpl;
28  import com.puppycrawl.tools.checkstyle.api.DetailAST;
29  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
30  
31  /**
32   * Contains utility methods for parser to use while creating ast.
33   */
34  public final class ParserUtil {
35  
36      /** Symbols with which javadoc starts. */
37      private static final String JAVADOC_START = "/**";
38      /** Symbols with which multiple comment starts. */
39      private static final String BLOCK_MULTIPLE_COMMENT_BEGIN = "/*";
40      /** Symbols with which multiple comment ends. */
41      private static final String BLOCK_MULTIPLE_COMMENT_END = "*/";
42  
43      /** Stop instances being created. **/
44      private ParserUtil() {
45      }
46  
47      /**
48       * Create block comment from string content.
49       *
50       * @param content comment content.
51       * @return DetailAST block comment
52       */
53      public static DetailAST createBlockCommentNode(String content) {
54          final DetailAstImpl blockCommentBegin = new DetailAstImpl();
55          blockCommentBegin.setType(TokenTypes.BLOCK_COMMENT_BEGIN);
56          blockCommentBegin.setText(BLOCK_MULTIPLE_COMMENT_BEGIN);
57          blockCommentBegin.setLineNo(0);
58          blockCommentBegin.setColumnNo(-JAVADOC_START.length());
59  
60          final DetailAstImpl commentContent = new DetailAstImpl();
61          commentContent.setType(TokenTypes.COMMENT_CONTENT);
62          commentContent.setText("*" + content);
63          commentContent.setLineNo(0);
64          // javadoc should starts at 0 column, so COMMENT_CONTENT node
65          // that contains javadoc identifier has -1 column
66          commentContent.setColumnNo(-1);
67  
68          final DetailAstImpl blockCommentEnd = new DetailAstImpl();
69          blockCommentEnd.setType(TokenTypes.BLOCK_COMMENT_END);
70          blockCommentEnd.setText(BLOCK_MULTIPLE_COMMENT_END);
71  
72          blockCommentBegin.setFirstChild(commentContent);
73          commentContent.setNextSibling(blockCommentEnd);
74          return blockCommentBegin;
75      }
76  
77      /**
78       * Create block comment from token.
79       *
80       * @param token Token object.
81       * @return DetailAST with BLOCK_COMMENT type.
82       */
83      public static DetailAST createBlockCommentNode(CommonToken token) {
84          final DetailAstImpl blockComment = new DetailAstImpl();
85          blockComment.initialize(TokenTypes.BLOCK_COMMENT_BEGIN, BLOCK_MULTIPLE_COMMENT_BEGIN);
86  
87          final int tokenCharPositionInLine = token.getCharPositionInLine();
88          final int tokenLine = token.getLine();
89          final String tokenText = token.getText();
90  
91          blockComment.setColumnNo(tokenCharPositionInLine);
92          blockComment.setLineNo(tokenLine);
93  
94          final DetailAstImpl blockCommentContent = new DetailAstImpl();
95          blockCommentContent.setType(TokenTypes.COMMENT_CONTENT);
96  
97          // Add length of '/*'
98          blockCommentContent.setColumnNo(tokenCharPositionInLine + 2);
99          blockCommentContent.setLineNo(tokenLine);
100         blockCommentContent.setText(tokenText);
101 
102         final DetailAstImpl blockCommentClose = new DetailAstImpl();
103         blockCommentClose.initialize(TokenTypes.BLOCK_COMMENT_END, BLOCK_MULTIPLE_COMMENT_END);
104 
105         final Map.Entry<Integer, Integer> linesColumns = countLinesColumns(
106                 tokenText, tokenLine, tokenCharPositionInLine + 1);
107         blockCommentClose.setLineNo(linesColumns.getKey());
108         blockCommentClose.setColumnNo(linesColumns.getValue());
109 
110         blockComment.addChild(blockCommentContent);
111         blockComment.addChild(blockCommentClose);
112         return blockComment;
113     }
114 
115     /**
116      * Count lines and columns (in last line) in text.
117      *
118      * @param text              String.
119      * @param initialLinesCnt   initial value of lines counter.
120      * @param initialColumnsCnt initial value of columns counter.
121      * @return entry(pair), key is line counter, value is column counter.
122      */
123     private static Map.Entry<Integer, Integer> countLinesColumns(
124         String text, int initialLinesCnt, int initialColumnsCnt) {
125         int lines = initialLinesCnt;
126         int columns = initialColumnsCnt;
127         boolean foundCr = false;
128         for (char c : text.toCharArray()) {
129             if (c == '\n') {
130                 foundCr = false;
131                 lines++;
132                 columns = 0;
133             }
134             else {
135                 if (foundCr) {
136                     foundCr = false;
137                     lines++;
138                     columns = 0;
139                 }
140                 if (c == '\r') {
141                     foundCr = true;
142                 }
143                 columns++;
144             }
145         }
146         if (foundCr) {
147             lines++;
148             columns = 0;
149         }
150         return new AbstractMap.SimpleEntry<>(lines, columns);
151     }
152 }