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