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