001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2024 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.checks.indentation;
021
022import com.puppycrawl.tools.checkstyle.api.DetailAST;
023import com.puppycrawl.tools.checkstyle.api.TokenTypes;
024
025/**
026 * Handler for annotation array initialization blocks.
027 *
028 */
029public class AnnotationArrayInitHandler extends BlockParentHandler {
030
031    /**
032     * Constant to define that the required character does not exist at any position.
033     */
034    private static final int NOT_EXIST = -1;
035
036    /**
037     * Construct an instance of this handler with the given indentation check,
038     * abstract syntax tree, and parent handler.
039     *
040     * @param indentCheck   the indentation check
041     * @param ast           the abstract syntax tree
042     * @param parent        the parent handler
043     */
044    public AnnotationArrayInitHandler(IndentationCheck indentCheck,
045                            DetailAST ast, AbstractExpressionHandler parent) {
046        super(indentCheck, "annotation array initialization", ast, parent);
047    }
048
049    @Override
050    protected IndentLevel getIndentImpl() {
051        final DetailAST parentAST = getMainAst().getParent();
052        return new IndentLevel(getLineStart(parentAST));
053    }
054
055    @Override
056    protected DetailAST getTopLevelAst() {
057        return null;
058    }
059
060    @Override
061    protected DetailAST getLeftCurly() {
062        return getMainAst();
063    }
064
065    @Override
066    protected IndentLevel curlyIndent() {
067        int offset = 0;
068
069        final DetailAST lcurly = getLeftCurly();
070        if (isOnStartOfLine(lcurly)) {
071            offset = getBraceAdjustment();
072        }
073
074        final IndentLevel level = new IndentLevel(getIndent(), offset);
075        return IndentLevel.addAcceptable(level, level.getLastIndentLevel()
076            + getLineWrappingIndentation());
077    }
078
079    @Override
080    protected DetailAST getRightCurly() {
081        return getMainAst().findFirstToken(TokenTypes.RCURLY);
082    }
083
084    @Override
085    protected boolean canChildrenBeNested() {
086        return true;
087    }
088
089    @Override
090    protected DetailAST getListChild() {
091        return getMainAst();
092    }
093
094    @Override
095    protected IndentLevel getChildrenExpectedIndent() {
096        IndentLevel expectedIndent =
097            new IndentLevel(getIndent(), getArrayInitIndentation(), getLineWrappingIndentation());
098
099        final DetailAST leftCurly = getLeftCurly();
100        final int firstLine = getFirstLine(getListChild());
101        final int lcurlyPos = expandedTabsColumnNo(leftCurly);
102        final int firstChildPos =
103            getNextFirstNonBlankOnLineAfter(firstLine, lcurlyPos);
104
105        // the code is written with old style where curlies are given their own line,
106        // the code block should be aligned to lcurly pos + lineWrappingIndent
107        if (firstChildPos == NOT_EXIST && lcurlyPos == getLineStart(leftCurly)) {
108            expectedIndent = IndentLevel.addAcceptable(expectedIndent,
109                        lcurlyPos + getLineWrappingIndentation());
110        }
111
112        if (firstChildPos != NOT_EXIST) {
113            expectedIndent = IndentLevel.addAcceptable(expectedIndent, firstChildPos, lcurlyPos
114                    + getLineWrappingIndentation());
115        }
116
117        return expectedIndent;
118    }
119
120    /**
121     * Returns column number of first non-blank char after
122     * specified column on specified line or -1 if
123     * such char doesn't exist.
124     *
125     * @param lineNo   number of line on which we search
126     * @param columnNo number of column after which we search
127     *
128     * @return column number of first non-blank char after
129     *         specified column on specified line or -1 if
130     *         such char doesn't exist.
131     */
132    private int getNextFirstNonBlankOnLineAfter(int lineNo, int columnNo) {
133        int realColumnNo = columnNo + 1;
134        final String line = getIndentCheck().getLines()[lineNo - 1];
135        final int lineLength = line.length();
136        while (realColumnNo < lineLength
137            && Character.isWhitespace(line.charAt(realColumnNo))) {
138            realColumnNo++;
139        }
140
141        if (realColumnNo == lineLength) {
142            realColumnNo = -1;
143        }
144        return realColumnNo;
145    }
146
147    /**
148     * A shortcut for {@code IndentationCheck} property.
149     *
150     * @return value of lineWrappingIndentation property
151     *         of {@code IndentationCheck}
152     */
153    private int getLineWrappingIndentation() {
154        return getIndentCheck().getLineWrappingIndentation();
155    }
156
157    /**
158     * A shortcut for {@code IndentationCheck} property.
159     *
160     * @return value of arrayInitIndent property
161     *         of {@code IndentationCheck}
162     */
163    private int getArrayInitIndentation() {
164        return getIndentCheck().getArrayInitIndent();
165    }
166
167}