001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2022 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        blockComment.setColumnNo(token.getCharPositionInLine());
088        blockComment.setLineNo(token.getLine());
089
090        final DetailAstImpl blockCommentContent = new DetailAstImpl();
091        blockCommentContent.setType(TokenTypes.COMMENT_CONTENT);
092
093        // Add length of '/*'
094        blockCommentContent.setColumnNo(token.getCharPositionInLine() + 2);
095        blockCommentContent.setLineNo(token.getLine());
096        blockCommentContent.setText(token.getText());
097
098        final DetailAstImpl blockCommentClose = new DetailAstImpl();
099        blockCommentClose.initialize(TokenTypes.BLOCK_COMMENT_END, BLOCK_MULTIPLE_COMMENT_END);
100
101        final Map.Entry<Integer, Integer> linesColumns = countLinesColumns(
102            token.getText(), token.getLine(), token.getCharPositionInLine() + 1);
103        blockCommentClose.setLineNo(linesColumns.getKey());
104        blockCommentClose.setColumnNo(linesColumns.getValue());
105
106        blockComment.addChild(blockCommentContent);
107        blockComment.addChild(blockCommentClose);
108        return blockComment;
109    }
110
111    /**
112     * Count lines and columns (in last line) in text.
113     *
114     * @param text              String.
115     * @param initialLinesCnt   initial value of lines counter.
116     * @param initialColumnsCnt initial value of columns counter.
117     * @return entry(pair), key is line counter, value is column counter.
118     */
119    private static Map.Entry<Integer, Integer> countLinesColumns(
120        String text, int initialLinesCnt, int initialColumnsCnt) {
121        int lines = initialLinesCnt;
122        int columns = initialColumnsCnt;
123        boolean foundCr = false;
124        for (char c : text.toCharArray()) {
125            if (c == '\n') {
126                foundCr = false;
127                lines++;
128                columns = 0;
129            }
130            else {
131                if (foundCr) {
132                    foundCr = false;
133                    lines++;
134                    columns = 0;
135                }
136                if (c == '\r') {
137                    foundCr = true;
138                }
139                columns++;
140            }
141        }
142        if (foundCr) {
143            lines++;
144            columns = 0;
145        }
146        return new AbstractMap.SimpleEntry<>(lines, columns);
147    }
148}