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