1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2024 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ///////////////////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle.checks.indentation;
21
22 import com.puppycrawl.tools.checkstyle.api.DetailAST;
23 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
24
25 /**
26 * Handler for annotation array initialization blocks.
27 *
28 */
29 public class AnnotationArrayInitHandler extends BlockParentHandler {
30
31 /**
32 * Constant to define that the required character does not exist at any position.
33 */
34 private static final int NOT_EXIST = -1;
35
36 /**
37 * Construct an instance of this handler with the given indentation check,
38 * abstract syntax tree, and parent handler.
39 *
40 * @param indentCheck the indentation check
41 * @param ast the abstract syntax tree
42 * @param parent the parent handler
43 */
44 public AnnotationArrayInitHandler(IndentationCheck indentCheck,
45 DetailAST ast, AbstractExpressionHandler parent) {
46 super(indentCheck, "annotation array initialization", ast, parent);
47 }
48
49 @Override
50 protected IndentLevel getIndentImpl() {
51 final DetailAST parentAST = getMainAst().getParent();
52 return new IndentLevel(getLineStart(parentAST));
53 }
54
55 @Override
56 protected DetailAST getTopLevelAst() {
57 return null;
58 }
59
60 @Override
61 protected DetailAST getLeftCurly() {
62 return getMainAst();
63 }
64
65 @Override
66 protected IndentLevel curlyIndent() {
67 int offset = 0;
68
69 final DetailAST lcurly = getLeftCurly();
70 if (isOnStartOfLine(lcurly)) {
71 offset = getBraceAdjustment();
72 }
73
74 final IndentLevel level = new IndentLevel(getIndent(), offset);
75 return IndentLevel.addAcceptable(level, level.getLastIndentLevel()
76 + getLineWrappingIndentation());
77 }
78
79 @Override
80 protected DetailAST getRightCurly() {
81 return getMainAst().findFirstToken(TokenTypes.RCURLY);
82 }
83
84 @Override
85 protected boolean canChildrenBeNested() {
86 return true;
87 }
88
89 @Override
90 protected DetailAST getListChild() {
91 return getMainAst();
92 }
93
94 @Override
95 protected IndentLevel getChildrenExpectedIndent() {
96 IndentLevel expectedIndent =
97 new IndentLevel(getIndent(), getArrayInitIndentation(), getLineWrappingIndentation());
98
99 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 }