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.indentation;
21
22 import java.util.Arrays;
23
24 import com.puppycrawl.tools.checkstyle.api.DetailAST;
25 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26 import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
27 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
28 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
29
30
31
32
33
34 public abstract class AbstractExpressionHandler {
35
36
37
38
39 private final IndentationCheck indentCheck;
40
41
42 private final DetailAST mainAst;
43
44
45 private final String typeName;
46
47
48 private final AbstractExpressionHandler parent;
49
50
51 private IndentLevel indent;
52
53
54
55
56
57
58
59
60
61
62 protected AbstractExpressionHandler(IndentationCheck indentCheck, String typeName,
63 DetailAST expr, AbstractExpressionHandler parent) {
64 this.indentCheck = indentCheck;
65 this.typeName = typeName;
66 mainAst = expr;
67 this.parent = parent;
68 }
69
70
71
72
73 public abstract void checkIndentation();
74
75
76
77
78
79
80
81
82
83
84
85 public final IndentLevel getIndent() {
86 if (indent == null) {
87 indent = getIndentImpl();
88 }
89 return indent;
90 }
91
92
93
94
95
96
97 protected IndentLevel getIndentImpl() {
98 return parent.getSuggestedChildIndent(this);
99 }
100
101
102
103
104
105
106
107
108
109
110
111
112 public IndentLevel getSuggestedChildIndent(AbstractExpressionHandler child) {
113 return new IndentLevel(getIndent(), getBasicOffset());
114 }
115
116
117
118
119
120
121
122
123 protected final void logError(DetailAST ast, String subtypeName,
124 int actualIndent) {
125 logError(ast, subtypeName, actualIndent, getIndent());
126 }
127
128
129
130
131
132
133
134
135
136 protected final void logError(DetailAST ast, String subtypeName,
137 int actualIndent, IndentLevel expectedIndent) {
138 final String typeStr;
139
140 if (subtypeName.isEmpty()) {
141 typeStr = "";
142 }
143 else {
144 typeStr = " " + subtypeName;
145 }
146 String messageKey = IndentationCheck.MSG_ERROR;
147 if (expectedIndent.isMultiLevel()) {
148 messageKey = IndentationCheck.MSG_ERROR_MULTI;
149 }
150 indentCheck.indentationLog(ast, messageKey,
151 typeName + typeStr, actualIndent, expectedIndent);
152 }
153
154
155
156
157
158
159
160
161 private void logChildError(DetailAST ast,
162 int actualIndent,
163 IndentLevel expectedIndent) {
164 String messageKey = IndentationCheck.MSG_CHILD_ERROR;
165 if (expectedIndent.isMultiLevel()) {
166 messageKey = IndentationCheck.MSG_CHILD_ERROR_MULTI;
167 }
168 indentCheck.indentationLog(ast, messageKey,
169 typeName, actualIndent, expectedIndent);
170 }
171
172
173
174
175
176
177
178
179 protected final boolean isOnStartOfLine(DetailAST ast) {
180 return getLineStart(ast) == expandedTabsColumnNo(ast);
181 }
182
183
184
185
186
187
188
189
190
191
192 public static DetailAST getFirstToken(DetailAST ast) {
193 DetailAST first = ast;
194 DetailAST child = ast.getFirstChild();
195
196 while (child != null) {
197 final DetailAST toTest = getFirstToken(child);
198 if (toTest.getColumnNo() < first.getColumnNo()) {
199 first = toTest;
200 }
201 child = child.getNextSibling();
202 }
203
204 return first;
205 }
206
207
208
209
210
211
212
213
214 protected final int getLineStart(DetailAST ast) {
215 return getLineStart(ast.getLineNo());
216 }
217
218
219
220
221
222
223
224
225 protected final int getLineStart(int lineNo) {
226 return getLineStart(indentCheck.getLine(lineNo - 1));
227 }
228
229
230
231
232
233
234
235
236 private int getLineStart(String line) {
237 int index = 0;
238 while (Character.isWhitespace(line.charAt(index))) {
239 index++;
240 }
241 return CommonUtil.lengthExpandedTabs(
242 line, index, indentCheck.getIndentationTabWidth());
243 }
244
245
246
247
248
249
250
251
252 protected boolean shouldIncreaseIndent() {
253 boolean result = true;
254 if (TokenUtil.isOfType(mainAst, TokenTypes.LITERAL_CATCH)) {
255 final DetailAST parameterAst = mainAst.findFirstToken(TokenTypes.PARAMETER_DEF);
256 result = !AnnotationUtil.containsAnnotation(parameterAst);
257 }
258 return result;
259 }
260
261
262
263
264
265
266
267
268
269
270 private void checkLinesIndent(DetailAstSet astSet,
271 IndentLevel indentLevel,
272 boolean firstLineMatches,
273 int firstLine,
274 boolean allowNesting) {
275 if (!astSet.isEmpty()) {
276
277 final DetailAST startLineAst = astSet.firstLine();
278 final int endLine = astSet.lastLine();
279 int startCol = expandedTabsColumnNo(astSet.firstLine());
280
281 final int realStartCol =
282 getLineStart(indentCheck.getLine(startLineAst.getLineNo() - 1));
283
284 if (firstLineMatches && !allowNesting) {
285 startCol = realStartCol;
286 }
287
288 if (realStartCol == startCol) {
289 checkLineIndent(startLineAst, indentLevel,
290 firstLineMatches);
291 }
292
293
294
295
296
297
298
299 IndentLevel theLevel = indentLevel;
300 if ((firstLineMatches || firstLine > mainAst.getLineNo())
301 && shouldIncreaseIndent()) {
302 theLevel = new IndentLevel(indentLevel, indentCheck.getLineWrappingIndentation());
303 }
304
305
306 for (int i = startLineAst.getLineNo() + 1; i <= endLine; i++) {
307 final Integer col = astSet.getStartColumn(i);
308
309
310
311
312 if (col != null) {
313 checkLineIndent(astSet.getAst(i), theLevel, false);
314 }
315 }
316 }
317 }
318
319
320
321
322
323
324
325
326 private void checkLineIndent(DetailAST ast,
327 IndentLevel indentLevel, boolean mustMatch) {
328 final String line = indentCheck.getLine(ast.getLineNo() - 1);
329 final int start = getLineStart(line);
330 final int columnNumber = expandedTabsColumnNo(ast);
331
332
333
334
335 if (mustMatch && !indentLevel.isAcceptable(start)
336 || !mustMatch && columnNumber == start && indentLevel.isGreaterThan(start)) {
337 logChildError(ast, start, indentLevel);
338 }
339 }
340
341
342
343
344
345
346
347
348 protected void checkWrappingIndentation(DetailAST firstNode, DetailAST lastNode) {
349 indentCheck.getLineWrappingHandler().checkIndentation(firstNode, lastNode);
350 }
351
352
353
354
355
356
357
358
359
360
361
362 protected void checkWrappingIndentation(DetailAST firstNode, DetailAST lastNode,
363 int wrappedIndentLevel, int startIndent, boolean ignoreFirstLine) {
364 indentCheck.getLineWrappingHandler().checkIndentation(firstNode, lastNode,
365 wrappedIndentLevel, startIndent,
366 LineWrappingHandler.LineWrappingOptions.ofBoolean(ignoreFirstLine));
367 }
368
369
370
371
372
373
374
375
376
377
378
379 protected final void checkChildren(DetailAST parentNode,
380 int[] tokenTypes,
381 IndentLevel startIndent,
382 boolean firstLineMatches,
383 boolean allowNesting) {
384 Arrays.sort(tokenTypes);
385 for (DetailAST child = parentNode.getFirstChild();
386 child != null;
387 child = child.getNextSibling()) {
388 if (Arrays.binarySearch(tokenTypes, child.getType()) >= 0) {
389 checkExpressionSubtree(child, startIndent,
390 firstLineMatches, allowNesting);
391 }
392 }
393 }
394
395
396
397
398
399
400
401
402
403 protected final void checkExpressionSubtree(
404 DetailAST tree,
405 IndentLevel indentLevel,
406 boolean firstLineMatches,
407 boolean allowNesting
408 ) {
409 final DetailAstSet subtreeAst = new DetailAstSet(indentCheck);
410 final int firstLine = getFirstLine(tree);
411 if (firstLineMatches && !allowNesting) {
412 final DetailAST firstAst = getFirstAstNode(tree);
413 subtreeAst.addAst(firstAst);
414 }
415 findSubtreeAst(subtreeAst, tree, allowNesting);
416
417 checkLinesIndent(subtreeAst, indentLevel, firstLineMatches, firstLine, allowNesting);
418 }
419
420
421
422
423
424
425
426 protected static int getFirstLine(DetailAST tree) {
427 return getFirstAstNode(tree).getLineNo();
428 }
429
430
431
432
433
434
435
436
437 protected static DetailAST getFirstAstNode(DetailAST ast) {
438
439 DetailAST curNode = ast;
440 DetailAST realStart = ast;
441 while (curNode != null) {
442 if (curNode.getLineNo() < realStart.getLineNo()
443 || curNode.getLineNo() == realStart.getLineNo()
444 && curNode.getColumnNo() < realStart.getColumnNo()) {
445 realStart = curNode;
446 }
447 DetailAST toVisit = curNode.getFirstChild();
448 while (curNode != ast && toVisit == null) {
449 toVisit = curNode.getNextSibling();
450 curNode = curNode.getParent();
451 }
452 curNode = toVisit;
453 }
454 return realStart;
455 }
456
457
458
459
460
461
462
463
464
465 protected final int expandedTabsColumnNo(DetailAST ast) {
466 final String line =
467 indentCheck.getLine(ast.getLineNo() - 1);
468
469 return CommonUtil.lengthExpandedTabs(line, ast.getColumnNo(),
470 indentCheck.getIndentationTabWidth());
471 }
472
473
474
475
476
477
478
479
480 protected final void findSubtreeAst(DetailAstSet astSet, DetailAST tree,
481 boolean allowNesting) {
482 if (!indentCheck.getHandlerFactory().isHandledType(tree.getType())) {
483 final int lineNum = tree.getLineNo();
484 final Integer colNum = astSet.getStartColumn(lineNum);
485
486 final int thisLineColumn = expandedTabsColumnNo(tree);
487 if (colNum == null || thisLineColumn < colNum) {
488 astSet.addAst(tree);
489 }
490
491
492 for (DetailAST node = tree.getFirstChild();
493 node != null;
494 node = node.getNextSibling()) {
495 findSubtreeAst(astSet, node, allowNesting);
496 }
497 }
498 }
499
500
501
502
503 protected void checkModifiers() {
504 final DetailAST modifiers =
505 mainAst.findFirstToken(TokenTypes.MODIFIERS);
506 for (DetailAST modifier = modifiers.getFirstChild();
507 modifier != null;
508 modifier = modifier.getNextSibling()) {
509 if (isOnStartOfLine(modifier)
510 && !getIndent().isAcceptable(expandedTabsColumnNo(modifier))) {
511 logError(modifier, "modifier",
512 expandedTabsColumnNo(modifier));
513 }
514 }
515 }
516
517
518
519
520
521
522 protected final IndentationCheck getIndentCheck() {
523 return indentCheck;
524 }
525
526
527
528
529
530
531 protected final DetailAST getMainAst() {
532 return mainAst;
533 }
534
535
536
537
538
539
540 protected final AbstractExpressionHandler getParent() {
541 return parent;
542 }
543
544
545
546
547
548
549 protected final int getBasicOffset() {
550 return indentCheck.getBasicOffset();
551 }
552
553
554
555
556
557
558
559 protected final int getBraceAdjustment() {
560 return indentCheck.getBraceAdjustment();
561 }
562
563
564
565
566
567
568
569 protected final void checkRightParen(DetailAST lparen, DetailAST rparen) {
570 if (rparen != null) {
571
572
573 final int rparenLevel = expandedTabsColumnNo(rparen);
574
575 final int lparenLevel = expandedTabsColumnNo(lparen);
576
577 if (rparenLevel != lparenLevel + 1
578 && !getIndent().isAcceptable(rparenLevel)
579 && isOnStartOfLine(rparen)) {
580 logError(rparen, "rparen", rparenLevel);
581 }
582 }
583 }
584
585
586
587
588
589
590 protected final void checkLeftParen(final DetailAST lparen) {
591
592
593 if (lparen != null
594 && !getIndent().isAcceptable(expandedTabsColumnNo(lparen))
595 && isOnStartOfLine(lparen)) {
596 logError(lparen, "lparen", expandedTabsColumnNo(lparen));
597 }
598 }
599
600 }