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")
37 public final class BracketsStructureCheck extends AbstractCheck {
38
39
40
41
42 public BracketsStructureCheck() {
43
44 }
45
46 @Override
47 public int[] getDefaultTokens() {
48 return new int[] {
49 TokenTypes.LITERAL_NEW,
50 TokenTypes.METHOD_CALL,
51 TokenTypes.RESOURCE_SPECIFICATION,
52 TokenTypes.ANNOTATION,
53 };
54 }
55
56 @Override
57 public int[] getAcceptableTokens() {
58 return this.getDefaultTokens();
59 }
60
61 @Override
62 public int[] getRequiredTokens() {
63 return this.getDefaultTokens();
64 }
65
66 @Override
67 public void visitToken(final DetailAST ast) {
68 if (ast.getType() == TokenTypes.RESOURCE_SPECIFICATION) {
69 this.checkResources(ast);
70 } else if (ast.getType() == TokenTypes.ANNOTATION) {
71 this.checkAnnotation(ast);
72 } else if (ast.getType() == TokenTypes.METHOD_CALL
73 || ast.getType() == TokenTypes.LITERAL_NEW) {
74 this.checkParams(ast);
75 } else {
76 final DetailAST brackets = ast.findFirstToken(TokenTypes.LPAREN);
77 if (brackets != null) {
78 this.checkParams(brackets);
79 }
80 }
81 }
82
83 private void checkParams(final DetailAST node) {
84 final DetailAST closing = node.findFirstToken(TokenTypes.RPAREN);
85 if (closing != null) {
86 this.checkLines(node, node.getLineNo(), closing.getLineNo());
87 }
88 }
89
90 private void checkLines(final DetailAST node, final int start,
91 final int end) {
92 if (start != end) {
93 final DetailAST elist = node.findFirstToken(TokenTypes.ELIST);
94 final int pline = BracketsStructureCheck.firstParamLine(elist);
95 if (pline == start) {
96 this.log(start, "Parameters should start on a new line");
97 }
98 this.checkExpressionList(elist, end);
99 }
100 }
101
102 private static int firstParamLine(final DetailAST elist) {
103 DetailAST leaf = elist;
104 while (leaf.getFirstChild() != null) {
105 leaf = leaf.getFirstChild();
106 }
107 final int line;
108 if (leaf.equals(elist)) {
109 line = elist.getLineNo();
110 } else {
111 line = leaf.getLineNo();
112 }
113 return line;
114 }
115
116 private void checkExpressionList(final DetailAST elist, final int end) {
117 if (elist.getChildCount() > 0) {
118 DetailAST last = elist.getLastChild();
119 while (last.getChildCount() > 0) {
120 last = last.getLastChild();
121 }
122 final int lline = last.getLineNo();
123 if (lline == end) {
124 this.log(lline, "Closing bracket should be on a new line");
125 }
126 }
127 }
128
129 private void checkAnnotation(final DetailAST node) {
130 final DetailAST opening = node.findFirstToken(TokenTypes.LPAREN);
131 final DetailAST closing = node.findFirstToken(TokenTypes.RPAREN);
132 if (opening != null && closing != null
133 && opening.getLineNo() != closing.getLineNo()) {
134 final DetailAST first = opening.getNextSibling();
135 final DetailAST last = closing.getPreviousSibling();
136 final DetailAST rcurly =
137 BracketsStructureCheck.arrayInitRcurly(first, last);
138 if (rcurly == null) {
139 this.checkBoundsStart(first, opening);
140 this.checkBoundsEnd(last, closing);
141 } else if (first.getLineNo() != rcurly.getLineNo()) {
142 this.checkArrayBounds(first, rcurly);
143 }
144 }
145 }
146
147 private static DetailAST arrayInitRcurly(final DetailAST first,
148 final DetailAST last) {
149 DetailAST rcurly = null;
150 if (first != null && first.equals(last)
151 && first.getType() == TokenTypes.ANNOTATION_ARRAY_INIT) {
152 final DetailAST candidate = first.getLastChild();
153 if (candidate != null
154 && candidate.getType() == TokenTypes.RCURLY) {
155 rcurly = candidate;
156 }
157 }
158 return rcurly;
159 }
160
161 private void checkArrayBounds(final DetailAST array,
162 final DetailAST rcurly) {
163 final DetailAST inner = array.getFirstChild();
164 if (!inner.equals(rcurly)) {
165 this.checkBoundsStart(inner, array);
166 }
167 this.checkBoundsEnd(rcurly.getPreviousSibling(), rcurly);
168 }
169
170 private void checkBoundsStart(final DetailAST first,
171 final DetailAST start) {
172 if (first != null && first.getLineNo() == start.getLineNo()) {
173 this.log(
174 first.getLineNo(),
175 "Parameters should start on a new line"
176 );
177 }
178 }
179
180 private void checkBoundsEnd(final DetailAST last, final DetailAST end) {
181 DetailAST leaf = last;
182 while (leaf != null && leaf.getChildCount() > 0) {
183 leaf = leaf.getLastChild();
184 }
185 if (leaf != null && leaf.getLineNo() == end.getLineNo()) {
186 this.log(
187 leaf.getLineNo(),
188 "Closing bracket should be on a new line"
189 );
190 }
191 }
192
193 private void checkResources(final DetailAST node) {
194 final DetailAST opening = node.findFirstToken(TokenTypes.LPAREN);
195 final DetailAST closing = node.findFirstToken(TokenTypes.RPAREN);
196 if (opening != null && closing != null
197 && opening.getLineNo() != closing.getLineNo()) {
198 this.checkResourceBody(node, opening, closing);
199 }
200 }
201
202 private void checkResourceBody(final DetailAST node,
203 final DetailAST opening, final DetailAST closing) {
204 final DetailAST resources = node.findFirstToken(TokenTypes.RESOURCES);
205 if (resources != null) {
206 if (resources.getLineNo() == opening.getLineNo()) {
207 this.log(
208 resources.getLineNo(),
209 "Parameters should start on a new line"
210 );
211 }
212 DetailAST last = resources.getLastChild();
213 while (last != null && last.getChildCount() > 0) {
214 last = last.getLastChild();
215 }
216 if (last != null && last.getLineNo() == closing.getLineNo()) {
217 this.log(
218 last.getLineNo(),
219 "Closing bracket should be on a new line"
220 );
221 }
222 }
223 }
224 }