001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2021 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 antlr.Token;
026import com.puppycrawl.tools.checkstyle.DetailAstImpl;
027import com.puppycrawl.tools.checkstyle.api.DetailAST;
028import com.puppycrawl.tools.checkstyle.api.TokenTypes;
029
030/**
031 * Contains utility methods for parser to use while creating ast.
032 */
033public final class ParserUtil {
034
035    /** Symbols with which javadoc starts. */
036    private static final String JAVADOC_START = "/**";
037    /** Symbols with which multiple comment starts. */
038    private static final String BLOCK_MULTIPLE_COMMENT_BEGIN = "/*";
039    /** Symbols with which multiple comment ends. */
040    private static final String BLOCK_MULTIPLE_COMMENT_END = "*/";
041
042    /** Stop instances being created. **/
043    private ParserUtil() {
044    }
045
046    /**
047     * Create block comment from string content.
048     *
049     * @param content comment content.
050     * @return DetailAST block comment
051     */
052    public static DetailAST createBlockCommentNode(String content) {
053        final DetailAstImpl blockCommentBegin = new DetailAstImpl();
054        blockCommentBegin.setType(TokenTypes.BLOCK_COMMENT_BEGIN);
055        blockCommentBegin.setText(BLOCK_MULTIPLE_COMMENT_BEGIN);
056        blockCommentBegin.setLineNo(0);
057        blockCommentBegin.setColumnNo(-JAVADOC_START.length());
058
059        final DetailAstImpl commentContent = new DetailAstImpl();
060        commentContent.setType(TokenTypes.COMMENT_CONTENT);
061        commentContent.setText("*" + content);
062        commentContent.setLineNo(0);
063        // javadoc should starts at 0 column, so COMMENT_CONTENT node
064        // that contains javadoc identifier has -1 column
065        commentContent.setColumnNo(-1);
066
067        final DetailAstImpl blockCommentEnd = new DetailAstImpl();
068        blockCommentEnd.setType(TokenTypes.BLOCK_COMMENT_END);
069        blockCommentEnd.setText(BLOCK_MULTIPLE_COMMENT_END);
070
071        blockCommentBegin.setFirstChild(commentContent);
072        commentContent.setNextSibling(blockCommentEnd);
073        return blockCommentBegin;
074    }
075
076    /**
077     * Create block comment from token.
078     *
079     * @param token Token object.
080     * @return DetailAST with BLOCK_COMMENT type.
081     */
082    public static DetailAST createBlockCommentNode(Token token) {
083        final DetailAstImpl blockComment = new DetailAstImpl();
084        blockComment.initialize(TokenTypes.BLOCK_COMMENT_BEGIN, BLOCK_MULTIPLE_COMMENT_BEGIN);
085
086        // column counting begins from 0
087        blockComment.setColumnNo(token.getColumn() - 1);
088        blockComment.setLineNo(token.getLine());
089
090        final DetailAstImpl blockCommentContent = new DetailAstImpl();
091        blockCommentContent.setType(TokenTypes.COMMENT_CONTENT);
092
093        // column counting begins from 0
094        // plus length of '/*'
095        blockCommentContent.setColumnNo(token.getColumn() - 1 + 2);
096        blockCommentContent.setLineNo(token.getLine());
097        blockCommentContent.setText(token.getText());
098
099        final DetailAstImpl blockCommentClose = new DetailAstImpl();
100        blockCommentClose.initialize(TokenTypes.BLOCK_COMMENT_END, BLOCK_MULTIPLE_COMMENT_END);
101
102        final Map.Entry<Integer, Integer> linesColumns = countLinesColumns(
103            token.getText(), token.getLine(), token.getColumn());
104        blockCommentClose.setLineNo(linesColumns.getKey());
105        blockCommentClose.setColumnNo(linesColumns.getValue());
106
107        blockComment.addChild(blockCommentContent);
108        blockComment.addChild(blockCommentClose);
109        return blockComment;
110    }
111
112    /**
113     * Count lines and columns (in last line) in text.
114     *
115     * @param text              String.
116     * @param initialLinesCnt   initial value of lines counter.
117     * @param initialColumnsCnt initial value of columns counter.
118     * @return entry(pair), key is line counter, value is column counter.
119     */
120    private static Map.Entry<Integer, Integer> countLinesColumns(
121        String text, int initialLinesCnt, int initialColumnsCnt) {
122        int lines = initialLinesCnt;
123        int columns = initialColumnsCnt;
124        boolean foundCr = false;
125        for (char c : text.toCharArray()) {
126            if (c == '\n') {
127                foundCr = false;
128                lines++;
129                columns = 0;
130            }
131            else {
132                if (foundCr) {
133                    foundCr = false;
134                    lines++;
135                    columns = 0;
136                }
137                if (c == '\r') {
138                    foundCr = true;
139                }
140                columns++;
141            }
142        }
143        if (foundCr) {
144            lines++;
145            columns = 0;
146        }
147        return new AbstractMap.SimpleEntry<>(lines, columns);
148    }
149}