1
2
3
4
5 package com.qulice.checkstyle;
6
7 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
8 import com.puppycrawl.tools.checkstyle.api.DetailAST;
9 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 @SuppressWarnings({"PMD.GodClass", "PMD.TooManyMethods"})
37 public final class BracketsStructureCheck extends AbstractCheck {
38
39 @Override
40 public int[] getDefaultTokens() {
41 return new int[] {
42 TokenTypes.LITERAL_NEW,
43 TokenTypes.METHOD_CALL,
44 TokenTypes.RESOURCE_SPECIFICATION,
45 TokenTypes.ANNOTATION,
46 };
47 }
48
49 @Override
50 public int[] getAcceptableTokens() {
51 return this.getDefaultTokens();
52 }
53
54 @Override
55 public int[] getRequiredTokens() {
56 return this.getDefaultTokens();
57 }
58
59 @Override
60 public void visitToken(final DetailAST ast) {
61 if (ast.getType() == TokenTypes.RESOURCE_SPECIFICATION) {
62 this.checkResources(ast);
63 } else if (ast.getType() == TokenTypes.ANNOTATION) {
64 this.checkAnnotation(ast);
65 } else if (ast.getType() == TokenTypes.METHOD_CALL
66 || ast.getType() == TokenTypes.LITERAL_NEW) {
67 this.checkParams(ast);
68 } else {
69 final DetailAST brackets = ast.findFirstToken(TokenTypes.LPAREN);
70 if (brackets != null) {
71 this.checkParams(brackets);
72 }
73 }
74 }
75
76
77
78
79
80 private void checkParams(final DetailAST node) {
81 final DetailAST closing = node.findFirstToken(TokenTypes.RPAREN);
82 if (closing != null) {
83 this.checkLines(node, node.getLineNo(), closing.getLineNo());
84 }
85 }
86
87
88
89
90
91
92
93 private void checkLines(final DetailAST node, final int start,
94 final int end) {
95 if (start != end) {
96 final DetailAST elist = node.findFirstToken(TokenTypes.ELIST);
97 final int pline = BracketsStructureCheck.firstParamLine(elist);
98 if (pline == start) {
99 this.log(start, "Parameters should start on a new line");
100 }
101 this.checkExpressionList(elist, end);
102 }
103 }
104
105
106
107
108
109
110
111
112
113 private static int firstParamLine(final DetailAST elist) {
114 DetailAST leaf = elist;
115 while (leaf.getFirstChild() != null) {
116 leaf = leaf.getFirstChild();
117 }
118 final int line;
119 if (leaf.equals(elist)) {
120 line = elist.getLineNo();
121 } else {
122 line = leaf.getLineNo();
123 }
124 return line;
125 }
126
127
128
129
130
131
132 private void checkExpressionList(final DetailAST elist, final int end) {
133 if (elist.getChildCount() > 0) {
134 DetailAST last = elist.getLastChild();
135 while (last.getChildCount() > 0) {
136 last = last.getLastChild();
137 }
138 final int lline = last.getLineNo();
139 if (lline == end) {
140 this.log(lline, "Closing bracket should be on a new line");
141 }
142 }
143 }
144
145
146
147
148
149 private void checkAnnotation(final DetailAST node) {
150 final DetailAST opening = node.findFirstToken(TokenTypes.LPAREN);
151 final DetailAST closing = node.findFirstToken(TokenTypes.RPAREN);
152 if (opening != null && closing != null
153 && opening.getLineNo() != closing.getLineNo()) {
154 final DetailAST first = opening.getNextSibling();
155 final DetailAST last = closing.getPreviousSibling();
156 final DetailAST rcurly =
157 BracketsStructureCheck.arrayInitRcurly(first, last);
158 if (rcurly == null) {
159 this.checkBoundsStart(first, opening);
160 this.checkBoundsEnd(last, closing);
161 } else if (first.getLineNo() != rcurly.getLineNo()) {
162 this.checkArrayBounds(first, rcurly);
163 }
164 }
165 }
166
167
168
169
170
171
172
173
174 private static DetailAST arrayInitRcurly(final DetailAST first,
175 final DetailAST last) {
176 DetailAST rcurly = null;
177 if (first != null && first.equals(last)
178 && first.getType() == TokenTypes.ANNOTATION_ARRAY_INIT) {
179 final DetailAST candidate = first.getLastChild();
180 if (candidate != null
181 && candidate.getType() == TokenTypes.RCURLY) {
182 rcurly = candidate;
183 }
184 }
185 return rcurly;
186 }
187
188
189
190
191
192
193
194
195 private void checkArrayBounds(final DetailAST array,
196 final DetailAST rcurly) {
197 final DetailAST inner = array.getFirstChild();
198 if (!inner.equals(rcurly)) {
199 this.checkBoundsStart(inner, array);
200 }
201 this.checkBoundsEnd(rcurly.getPreviousSibling(), rcurly);
202 }
203
204
205
206
207
208
209
210 private void checkBoundsStart(final DetailAST first,
211 final DetailAST start) {
212 if (first != null && first.getLineNo() == start.getLineNo()) {
213 this.log(
214 first.getLineNo(),
215 "Parameters should start on a new line"
216 );
217 }
218 }
219
220
221
222
223
224
225
226 private void checkBoundsEnd(final DetailAST last, final DetailAST end) {
227 DetailAST leaf = last;
228 while (leaf != null && leaf.getChildCount() > 0) {
229 leaf = leaf.getLastChild();
230 }
231 if (leaf != null && leaf.getLineNo() == end.getLineNo()) {
232 this.log(
233 leaf.getLineNo(),
234 "Closing bracket should be on a new line"
235 );
236 }
237 }
238
239
240
241
242
243 private void checkResources(final DetailAST node) {
244 final DetailAST opening = node.findFirstToken(TokenTypes.LPAREN);
245 final DetailAST closing = node.findFirstToken(TokenTypes.RPAREN);
246 if (opening != null && closing != null
247 && opening.getLineNo() != closing.getLineNo()) {
248 this.checkResourceBody(node, opening, closing);
249 }
250 }
251
252
253
254
255
256
257
258 private void checkResourceBody(final DetailAST node,
259 final DetailAST opening, final DetailAST closing) {
260 final DetailAST resources = node.findFirstToken(TokenTypes.RESOURCES);
261 if (resources != null) {
262 if (resources.getLineNo() == opening.getLineNo()) {
263 this.log(
264 resources.getLineNo(),
265 "Parameters should start on a new line"
266 );
267 }
268 DetailAST last = resources.getLastChild();
269 while (last != null && last.getChildCount() > 0) {
270 last = last.getLastChild();
271 }
272 if (last != null && last.getLineNo() == closing.getLineNo()) {
273 this.log(
274 last.getLineNo(),
275 "Closing bracket should be on a new line"
276 );
277 }
278 }
279 }
280 }