1
2
3
4
5 package com.qulice.checkstyle;
6
7 import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
8 import com.puppycrawl.tools.checkstyle.api.FileText;
9 import java.io.File;
10 import org.cactoos.text.Joined;
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 public final class CascadeIndentationCheck extends AbstractFileSetCheck {
30
31
32
33
34 public CascadeIndentationCheck() {
35
36 }
37
38 @Override
39 public void processFiltered(final File file, final FileText lines) {
40 int previous = 0;
41 boolean closer = false;
42 for (int pos = 0; pos < lines.size(); pos += 1) {
43 final String line = lines.get(pos);
44 final int current = CascadeIndentationCheck.indentation(line);
45 if (CascadeIndentationCheck.inCommentBlock(line)
46 || line.isEmpty()) {
47 continue;
48 }
49 final String message = CascadeIndentationCheck.violation(
50 line, current, previous, closer
51 );
52 if (!message.isEmpty()) {
53 this.log(pos + 1, message);
54 }
55 previous = current;
56 closer = CascadeIndentationCheck.isClosingBracketLine(line);
57 }
58 }
59
60 private static String violation(final String line, final int current,
61 final int previous, final boolean closer) {
62 final String message;
63 if (current > previous
64 && current != previous + 4) {
65 message = String.format(
66 new Joined(
67 "",
68 "Indentation (%d) must be same or ",
69 "less than previous line (%d), or ",
70 "bigger by exactly 4"
71 ).toString(),
72 current,
73 previous
74 );
75 } else if (closer && current > previous) {
76 message = String.format(
77 new Joined(
78 "",
79 "Indentation (%d) must not be greater ",
80 "than the closing bracket line (%d)"
81 ).toString(),
82 current,
83 previous
84 );
85 } else if (closer && current == previous
86 && CascadeIndentationCheck.isClosingBracketLine(line)) {
87 message = String.format(
88 new Joined(
89 "",
90 "Indentation (%d) of this closing bracket line must be ",
91 "less than the one of the closing bracket line above it"
92 ).toString(),
93 current
94 );
95 } else {
96 message = "";
97 }
98 return message;
99 }
100
101 private static boolean isClosingBracketLine(final String line) {
102 final String trimmed = line.trim();
103 boolean result = !trimmed.isEmpty()
104 && CascadeIndentationCheck.isClosingBracket(trimmed.charAt(0));
105 for (int idx = 0; result && idx < trimmed.length(); idx += 1) {
106 result = CascadeIndentationCheck.isAllowedTail(trimmed.charAt(idx));
107 }
108 return result;
109 }
110
111 private static boolean isClosingBracket(final char chr) {
112 return chr == ')' || chr == ']' || chr == '}';
113 }
114
115 private static boolean isAllowedTail(final char chr) {
116 return CascadeIndentationCheck.isClosingBracket(chr)
117 || chr == ';' || chr == ','
118 || Character.isWhitespace(chr);
119 }
120
121 private static boolean inCommentBlock(final String line) {
122 final String trimmed = line.trim();
123 return !trimmed.isEmpty()
124 && (trimmed.charAt(0) == '*'
125 || trimmed.startsWith("/*")
126 || trimmed.startsWith("*/")
127 );
128 }
129
130 private static int indentation(final String line) {
131 int result = 0;
132 for (int pos = 0; pos < line.length(); pos += 1) {
133 if (!Character.isWhitespace(line.charAt(pos))) {
134 break;
135 }
136 result += 1;
137 }
138 return result;
139 }
140 }