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