1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks.whitespace;
21
22 import java.util.BitSet;
23
24 import com.puppycrawl.tools.checkstyle.api.DetailAST;
25 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
27 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 public class ParenPadCheck extends AbstractParenPadCheck {
50
51
52
53
54 private final BitSet acceptableTokens;
55
56
57
58
59 public ParenPadCheck() {
60 acceptableTokens = TokenUtil.asBitSet(makeAcceptableTokens());
61 }
62
63 @Override
64 public int[] getDefaultTokens() {
65 return makeAcceptableTokens();
66 }
67
68 @Override
69 public int[] getAcceptableTokens() {
70 return makeAcceptableTokens();
71 }
72
73 @Override
74 public int[] getRequiredTokens() {
75 return CommonUtil.EMPTY_INT_ARRAY;
76 }
77
78 @Override
79 public void visitToken(DetailAST ast) {
80 switch (ast.getType()) {
81 case TokenTypes.METHOD_CALL -> {
82 processLeft(ast);
83 processRight(ast.findFirstToken(TokenTypes.RPAREN));
84 }
85
86 case TokenTypes.DOT, TokenTypes.EXPR, TokenTypes.QUESTION -> processExpression(ast);
87
88 case TokenTypes.LITERAL_FOR -> visitLiteralFor(ast);
89
90 case TokenTypes.ANNOTATION,
91 TokenTypes.ENUM_CONSTANT_DEF,
92 TokenTypes.LITERAL_NEW,
93 TokenTypes.LITERAL_SYNCHRONIZED,
94 TokenTypes.LAMBDA -> visitTokenWithOptionalParentheses(ast);
95
96 case TokenTypes.RESOURCE_SPECIFICATION -> visitResourceSpecification(ast);
97
98 default -> {
99 processLeft(ast.findFirstToken(TokenTypes.LPAREN));
100 processRight(ast.findFirstToken(TokenTypes.RPAREN));
101 }
102 }
103 }
104
105
106
107
108
109
110
111
112
113 private void visitTokenWithOptionalParentheses(DetailAST ast) {
114 final DetailAST parenAst = ast.findFirstToken(TokenTypes.LPAREN);
115 if (parenAst != null) {
116 processLeft(parenAst);
117 processRight(ast.findFirstToken(TokenTypes.RPAREN));
118 }
119 }
120
121
122
123
124
125
126 private void visitResourceSpecification(DetailAST ast) {
127 processLeft(ast.findFirstToken(TokenTypes.LPAREN));
128 final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
129 if (!hasPrecedingSemiColon(rparen)) {
130 processRight(rparen);
131 }
132 }
133
134
135
136
137
138
139
140 private static boolean hasPrecedingSemiColon(DetailAST ast) {
141 return ast.getPreviousSibling().getType() == TokenTypes.SEMI;
142 }
143
144
145
146
147
148
149 private void visitLiteralFor(DetailAST ast) {
150 final DetailAST lparen = ast.findFirstToken(TokenTypes.LPAREN);
151 if (!isPrecedingEmptyForInit(lparen)) {
152 processLeft(lparen);
153 }
154 final DetailAST rparen = ast.findFirstToken(TokenTypes.RPAREN);
155 if (!isFollowsEmptyForIterator(rparen)) {
156 processRight(rparen);
157 }
158 }
159
160
161
162
163
164
165
166 private void processExpression(DetailAST ast) {
167 DetailAST currentNode = ast.getFirstChild();
168 while (currentNode != null) {
169 if (currentNode.getType() == TokenTypes.LPAREN) {
170 processLeft(currentNode);
171 }
172 else if (currentNode.getType() == TokenTypes.RPAREN && !isInTypecast(currentNode)) {
173 processRight(currentNode);
174 }
175 else if (currentNode.hasChildren() && !isAcceptableToken(currentNode)) {
176
177
178 currentNode = currentNode.getFirstChild();
179 continue;
180 }
181
182
183 while (currentNode.getNextSibling() == null && currentNode.getParent() != ast) {
184 currentNode = currentNode.getParent();
185 }
186 currentNode = currentNode.getNextSibling();
187 }
188 }
189
190
191
192
193
194
195
196 private boolean isAcceptableToken(DetailAST ast) {
197 return acceptableTokens.get(ast.getType());
198 }
199
200
201
202
203
204
205 private static int[] makeAcceptableTokens() {
206 return new int[] {TokenTypes.ANNOTATION,
207 TokenTypes.ANNOTATION_FIELD_DEF,
208 TokenTypes.CTOR_CALL,
209 TokenTypes.CTOR_DEF,
210 TokenTypes.DOT,
211 TokenTypes.ENUM_CONSTANT_DEF,
212 TokenTypes.EXPR,
213 TokenTypes.LITERAL_CATCH,
214 TokenTypes.LITERAL_DO,
215 TokenTypes.LITERAL_FOR,
216 TokenTypes.LITERAL_IF,
217 TokenTypes.LITERAL_NEW,
218 TokenTypes.LITERAL_SWITCH,
219 TokenTypes.LITERAL_SYNCHRONIZED,
220 TokenTypes.LITERAL_WHILE,
221 TokenTypes.METHOD_CALL,
222 TokenTypes.METHOD_DEF,
223 TokenTypes.QUESTION,
224 TokenTypes.RESOURCE_SPECIFICATION,
225 TokenTypes.SUPER_CTOR_CALL,
226 TokenTypes.LAMBDA,
227 TokenTypes.RECORD_DEF,
228 TokenTypes.RECORD_PATTERN_DEF,
229 };
230 }
231
232
233
234
235
236
237
238
239 private static boolean isInTypecast(DetailAST ast) {
240 boolean result = false;
241 if (ast.getParent().getType() == TokenTypes.TYPECAST) {
242 final DetailAST firstRparen = ast.getParent().findFirstToken(TokenTypes.RPAREN);
243 if (TokenUtil.areOnSameLine(firstRparen, ast)
244 && firstRparen.getColumnNo() == ast.getColumnNo()) {
245 result = true;
246 }
247 }
248 return result;
249 }
250
251
252
253
254
255
256
257 private static boolean isFollowsEmptyForIterator(DetailAST ast) {
258 boolean result = false;
259 final DetailAST parent = ast.getParent();
260
261 if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
262 final DetailAST forIterator =
263 parent.findFirstToken(TokenTypes.FOR_ITERATOR);
264 result = !forIterator.hasChildren();
265 }
266 return result;
267 }
268
269
270
271
272
273
274
275 private static boolean isPrecedingEmptyForInit(DetailAST ast) {
276 boolean result = false;
277 final DetailAST parent = ast.getParent();
278
279 if (parent.findFirstToken(TokenTypes.FOR_EACH_CLAUSE) == null) {
280 final DetailAST forIterator =
281 parent.findFirstToken(TokenTypes.FOR_INIT);
282 result = !forIterator.hasChildren();
283 }
284 return result;
285 }
286
287 }