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.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 int firstLine = getFirstLine(getListChild());
100        final int lcurlyPos = expandedTabsColumnNo(getLeftCurly());
101        final int firstChildPos =
102            getNextFirstNonBlankOnLineAfter(firstLine, lcurlyPos);
103
104        if (firstChildPos != NOT_EXIST) {
105            expectedIndent = IndentLevel.addAcceptable(expectedIndent, firstChildPos, lcurlyPos
106                    + getLineWrappingIndentation());
107        }
108        return expectedIndent;
109    }
110
111    /**
112     * Returns column number of first non-blank char after
113     * specified column on specified line or -1 if
114     * such char doesn't exist.
115     *
116     * @param lineNo   number of line on which we search
117     * @param columnNo number of column after which we search
118     *
119     * @return column number of first non-blank char after
120     *         specified column on specified line or -1 if
121     *         such char doesn't exist.
122     */
123    private int getNextFirstNonBlankOnLineAfter(int lineNo, int columnNo) {
124        int realColumnNo = columnNo + 1;
125        final String line = getIndentCheck().getLines()[lineNo - 1];
126        final int lineLength = line.length();
127        while (realColumnNo < lineLength
128            && Character.isWhitespace(line.charAt(realColumnNo))) {
129            realColumnNo++;
130        }
131
132        if (realColumnNo == lineLength) {
133            realColumnNo = -1;
134        }
135        return realColumnNo;
136    }
137
138    /**
139     * A shortcut for {@code IndentationCheck} property.
140     *
141     * @return value of lineWrappingIndentation property
142     *         of {@code IndentationCheck}
143     */
144    private int getLineWrappingIndentation() {
145        return getIndentCheck().getLineWrappingIndentation();
146    }
147
148    /**
149     * A shortcut for {@code IndentationCheck} property.
150     *
151     * @return value of arrayInitIndent property
152     *         of {@code IndentationCheck}
153     */
154    private int getArrayInitIndentation() {
155        return getIndentCheck().getArrayInitIndent();
156    }
157
158}