001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2025 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.utils;
021
022import java.util.AbstractMap;
023import java.util.Map;
024
025import org.antlr.v4.runtime.CommonToken;
026
027import com.puppycrawl.tools.checkstyle.DetailAstImpl;
028import com.puppycrawl.tools.checkstyle.api.DetailAST;
029import com.puppycrawl.tools.checkstyle.api.TokenTypes;
030
031/**
032 * Contains utility methods for parser to use while creating ast.
033 */
034public final class ParserUtil {
035
036 /** Symbols with which javadoc starts. */
037 private static final String JAVADOC_START = "/**";
038 /** Symbols with which multiple comment starts. */
039 private static final String BLOCK_MULTIPLE_COMMENT_BEGIN = "/*";
040 /** Symbols with which multiple comment ends. */
041 private static final String BLOCK_MULTIPLE_COMMENT_END = "*/";
042
043 /** Stop instances being created. **/
044 private ParserUtil() {
045 }
046
047 /**
048 * Create block comment from string content.
049 *
050 * @param content comment content.
051 * @return DetailAST block comment
052 */
053 public static DetailAST createBlockCommentNode(String content) {
054 final DetailAstImpl blockCommentBegin = new DetailAstImpl();
055 blockCommentBegin.setType(TokenTypes.BLOCK_COMMENT_BEGIN);
056 blockCommentBegin.setText(BLOCK_MULTIPLE_COMMENT_BEGIN);
057 blockCommentBegin.setLineNo(0);
058 blockCommentBegin.setColumnNo(-JAVADOC_START.length());
059
060 final DetailAstImpl commentContent = new DetailAstImpl();
061 commentContent.setType(TokenTypes.COMMENT_CONTENT);
062 commentContent.setText("*" + content);
063 commentContent.setLineNo(0);
064 // javadoc should starts at 0 column, so COMMENT_CONTENT node
065 // that contains javadoc identifier has -1 column
066 commentContent.setColumnNo(-1);
067
068 final DetailAstImpl blockCommentEnd = new DetailAstImpl();
069 blockCommentEnd.setType(TokenTypes.BLOCK_COMMENT_END);
070 blockCommentEnd.setText(BLOCK_MULTIPLE_COMMENT_END);
071
072 blockCommentBegin.setFirstChild(commentContent);
073 commentContent.setNextSibling(blockCommentEnd);
074 return blockCommentBegin;
075 }
076
077 /**
078 * Create block comment from token.
079 *
080 * @param token Token object.
081 * @return DetailAST with BLOCK_COMMENT type.
082 */
083 public static DetailAST createBlockCommentNode(CommonToken token) {
084 final DetailAstImpl blockComment = new DetailAstImpl();
085 blockComment.initialize(TokenTypes.BLOCK_COMMENT_BEGIN, BLOCK_MULTIPLE_COMMENT_BEGIN);
086
087 final int tokenCharPositionInLine = token.getCharPositionInLine();
088 final int tokenLine = token.getLine();
089 final String tokenText = token.getText();
090
091 blockComment.setColumnNo(tokenCharPositionInLine);
092 blockComment.setLineNo(tokenLine);
093
094 final DetailAstImpl blockCommentContent = new DetailAstImpl();
095 blockCommentContent.setType(TokenTypes.COMMENT_CONTENT);
096
097 // Add length of '/*'
098 blockCommentContent.setColumnNo(tokenCharPositionInLine + 2);
099 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}