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 int startCol = expandedTabsColumnNo(startLineAst);
279
280 final int realStartCol =
281 getLineStart(indentCheck.getLine(startLineAst.getLineNo() - 1));
282
283 if (firstLineMatches && !allowNesting) {
284 startCol = realStartCol;
285 }
286
287 if (realStartCol == startCol) {
288 checkLineIndent(startLineAst, indentLevel,
289 firstLineMatches);
290 }
291
292 checkRemainingLines(firstLineMatches, indentLevel, firstLine, astSet);
293
294 }
295 }
296
297
298
299
300
301
302
303
304
305 private void checkRemainingLines(boolean firstLineMatches,
306 IndentLevel indentLevel,
307 int firstLine,
308 DetailAstSet astSet) {
309
310
311
312
313
314 final DetailAST startLineAst = astSet.firstLine();
315 final int endLine = astSet.lastLine();
316 IndentLevel level = indentLevel;
317
318 if (shouldIncreaseIndent()
319 && startLineAst.getType() != TokenTypes.ANNOTATION
320 && (firstLineMatches || firstLine > mainAst.getLineNo())) {
321 level = new IndentLevel(indentLevel,
322 indentCheck.getLineWrappingIndentation());
323 }
324
325
326 for (int index = startLineAst.getLineNo() + 1; index <= endLine; index++) {
327 final Integer col = astSet.getStartColumn(index);
328
329
330
331
332 if (col != null) {
333 checkLineIndent(astSet.getAst(index), level, false);
334 }
335 }
336 }
337
338
339
340
341
342
343
344
345 private void checkLineIndent(DetailAST ast,
346 IndentLevel indentLevel, boolean mustMatch) {
347 final String line = indentCheck.getLine(ast.getLineNo() - 1);
348 final int start = getLineStart(line);
349 final int columnNumber = expandedTabsColumnNo(ast);
350
351
352
353
354 if (mustMatch && !indentLevel.isAcceptable(start)
355 || !mustMatch && columnNumber == start && indentLevel.isGreaterThan(start)) {
356 logChildError(ast, start, indentLevel);
357 }
358 }
359
360
361
362
363
364
365
366
367 protected void checkWrappingIndentation(DetailAST firstNode, DetailAST lastNode) {
368 indentCheck.getLineWrappingHandler().checkIndentation(firstNode, lastNode);
369 }
370
371
372
373
374
375
376
377
378
379
380
381 protected void checkWrappingIndentation(DetailAST firstNode, DetailAST lastNode,
382 int wrappedIndentLevel, int startIndent, boolean ignoreFirstLine) {
383 indentCheck.getLineWrappingHandler().checkIndentation(firstNode, lastNode,
384 wrappedIndentLevel, startIndent,
385 LineWrappingHandler.LineWrappingOptions.ofBoolean(ignoreFirstLine));
386 }
387
388
389
390
391
392
393
394
395
396
397
398 protected final void checkChildren(DetailAST parentNode,
399 int[] tokenTypes,
400 IndentLevel startIndent,
401 boolean firstLineMatches,
402 boolean allowNesting) {
403 Arrays.sort(tokenTypes);
404 for (DetailAST child = parentNode.getFirstChild();
405 child != null;
406 child = child.getNextSibling()) {
407 if (Arrays.binarySearch(tokenTypes, child.getType()) >= 0) {
408 checkExpressionSubtree(child, startIndent,
409 firstLineMatches, allowNesting);
410 }
411 }
412 }
413
414
415
416
417
418
419
420
421
422 protected final void checkExpressionSubtree(
423 DetailAST tree,
424 IndentLevel indentLevel,
425 boolean firstLineMatches,
426 boolean allowNesting
427 ) {
428 final DetailAstSet subtreeAst = new DetailAstSet(indentCheck);
429 final int firstLine = getFirstLine(tree);
430 if (firstLineMatches && !allowNesting) {
431 final DetailAST firstAst = getFirstAstNode(tree);
432 subtreeAst.addAst(firstAst);
433 }
434 findSubtreeAst(subtreeAst, tree, allowNesting);
435
436 checkLinesIndent(subtreeAst, indentLevel, firstLineMatches, firstLine, allowNesting);
437 }
438
439
440
441
442
443
444
445 protected static int getFirstLine(DetailAST tree) {
446 return getFirstAstNode(tree).getLineNo();
447 }
448
449
450
451
452
453
454
455
456 protected static DetailAST getFirstAstNode(DetailAST ast) {
457
458 DetailAST curNode = ast;
459 DetailAST realStart = ast;
460 while (curNode != null) {
461 if (curNode.getLineNo() < realStart.getLineNo()
462 || curNode.getLineNo() == realStart.getLineNo()
463 && curNode.getColumnNo() < realStart.getColumnNo()) {
464 realStart = curNode;
465 }
466 DetailAST toVisit = curNode.getFirstChild();
467 while (curNode != ast && toVisit == null) {
468 toVisit = curNode.getNextSibling();
469 curNode = curNode.getParent();
470 }
471 curNode = toVisit;
472 }
473 return realStart;
474 }
475
476
477
478
479
480
481
482
483
484 protected final int expandedTabsColumnNo(DetailAST ast) {
485 final String line =
486 indentCheck.getLine(ast.getLineNo() - 1);
487
488 return CommonUtil.lengthExpandedTabs(line, ast.getColumnNo(),
489 indentCheck.getIndentationTabWidth());
490 }
491
492
493
494
495
496
497
498
499 protected final void findSubtreeAst(DetailAstSet astSet, DetailAST tree,
500 boolean allowNesting) {
501 if (!indentCheck.getHandlerFactory().isHandledType(tree.getType())) {
502 final int lineNum = tree.getLineNo();
503 final Integer colNum = astSet.getStartColumn(lineNum);
504
505 final int thisLineColumn = expandedTabsColumnNo(tree);
506 if (colNum == null || thisLineColumn < colNum) {
507 astSet.addAst(tree);
508 }
509
510
511 for (DetailAST node = tree.getFirstChild();
512 node != null;
513 node = node.getNextSibling()) {
514 findSubtreeAst(astSet, node, allowNesting);
515 }
516 }
517 }
518
519
520
521
522 protected void checkModifiers() {
523 final DetailAST modifiers =
524 mainAst.findFirstToken(TokenTypes.MODIFIERS);
525 for (DetailAST modifier = modifiers.getFirstChild();
526 modifier != null;
527 modifier = modifier.getNextSibling()) {
528 if (isOnStartOfLine(modifier)
529 && !getIndent().isAcceptable(expandedTabsColumnNo(modifier))) {
530 logError(modifier, "modifier",
531 expandedTabsColumnNo(modifier));
532 }
533 }
534 }
535
536
537
538
539
540
541 protected final IndentationCheck getIndentCheck() {
542 return indentCheck;
543 }
544
545
546
547
548
549
550 protected final DetailAST getMainAst() {
551 return mainAst;
552 }
553
554
555
556
557
558
559 protected final AbstractExpressionHandler getParent() {
560 return parent;
561 }
562
563
564
565
566
567
568 protected final int getBasicOffset() {
569 return indentCheck.getBasicOffset();
570 }
571
572
573
574
575
576
577
578 protected final int getBraceAdjustment() {
579 return indentCheck.getBraceAdjustment();
580 }
581
582
583
584
585
586
587
588 protected final void checkRightParen(DetailAST lparen, DetailAST rparen) {
589 if (rparen != null) {
590
591
592 final int rparenLevel = expandedTabsColumnNo(rparen);
593
594 final int lparenLevel = expandedTabsColumnNo(lparen);
595
596 if (rparenLevel != lparenLevel + 1
597 && !getIndent().isAcceptable(rparenLevel)
598 && isOnStartOfLine(rparen)) {
599 logError(rparen, "rparen", rparenLevel);
600 }
601 }
602 }
603
604
605
606
607
608
609 protected final void checkLeftParen(final DetailAST lparen) {
610
611
612 if (lparen != null
613 && !getIndent().isAcceptable(expandedTabsColumnNo(lparen))
614 && isOnStartOfLine(lparen)) {
615 logError(lparen, "lparen", expandedTabsColumnNo(lparen));
616 }
617 }
618
619 }