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 java.util.Collection;
22 import java.util.Iterator;
23 import java.util.NavigableMap;
24 import java.util.TreeMap;
25
26 import com.puppycrawl.tools.checkstyle.api.DetailAST;
27 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
28
29
30
31
32
33
34
35
36
37 public class LineWrappingHandler
38 {
39
40
41
42
43
44
45 private final IndentationCheck indentCheck;
46
47
48
49
50 private DetailAST firstNode;
51
52
53
54
55 private DetailAST lastNode;
56
57
58
59
60 private int indentLevel;
61
62
63
64
65 private boolean forceStrictCondition;
66
67
68
69
70
71
72
73
74
75
76
77 public LineWrappingHandler(IndentationCheck instance, DetailAST firstNode, DetailAST lastNode)
78 {
79 indentCheck = instance;
80 this.firstNode = firstNode;
81 this.lastNode = lastNode;
82 indentLevel = indentCheck.getLineWrappingIndentation();
83 forceStrictCondition = indentCheck.getForceStrictCondition();
84 }
85
86
87
88
89 protected int getCurrentIndentation()
90 {
91 return firstNode.getColumnNo() + indentLevel;
92 }
93
94
95
96 protected final DetailAST getFirstNode()
97 {
98 return firstNode;
99 }
100
101 protected final DetailAST getLastNode()
102 {
103 return lastNode;
104 }
105
106 protected final int getIndentLevel()
107 {
108 return indentLevel;
109 }
110
111
112
113
114 public void checkIndentation()
115 {
116 final NavigableMap<Integer, DetailAST> firstNodesOnLines = collectFirstNodes();
117
118 final DetailAST firstNode = firstNodesOnLines.get(firstNodesOnLines.firstKey());
119 if (firstNode.getType() == TokenTypes.AT) {
120 checkAnnotationIndentation(firstNode, firstNodesOnLines);
121 }
122
123
124 firstNodesOnLines.remove(firstNodesOnLines.firstKey());
125 final int firstNodeIndent = getFirstNodeIndent(firstNode);
126 final int currentIndent = firstNodeIndent + indentLevel;
127
128 for (DetailAST node : firstNodesOnLines.values()) {
129 final int currentType = node.getType();
130
131 if (currentType == TokenTypes.RCURLY
132 || currentType == TokenTypes.RPAREN
133 || currentType == TokenTypes.ARRAY_INIT)
134 {
135 logWarningMessage(node, firstNodeIndent);
136 }
137 else if (currentType == TokenTypes.LITERAL_IF) {
138 final DetailAST parent = node.getParent();
139
140 if (parent.getType() == TokenTypes.LITERAL_ELSE) {
141 logWarningMessage(parent, currentIndent);
142 }
143 }
144 else {
145 logWarningMessage(node, currentIndent);
146 }
147 }
148 }
149
150
151
152
153
154
155
156
157 private int getFirstNodeIndent(DetailAST node)
158 {
159 int indentLevel = node.getColumnNo();
160
161 if (node.getType() == TokenTypes.LITERAL_IF
162 && node.getParent().getType() == TokenTypes.LITERAL_ELSE)
163 {
164 final DetailAST lcurly = node.getParent().getPreviousSibling();
165 final DetailAST rcurly = lcurly.getLastChild();
166
167 if (lcurly.getType() == TokenTypes.SLIST
168 && rcurly.getLineNo() == node.getLineNo())
169 {
170 indentLevel = rcurly.getColumnNo();
171 }
172 else {
173 indentLevel = node.getParent().getColumnNo();
174 }
175 }
176 return indentLevel;
177 }
178
179
180
181
182
183
184
185 private NavigableMap<Integer, DetailAST> collectFirstNodes()
186 {
187 final NavigableMap<Integer, DetailAST> result = new TreeMap<>();
188
189 result.put(firstNode.getLineNo(), firstNode);
190 DetailAST curNode = firstNode.getFirstChild();
191
192 while (curNode != null && curNode != lastNode) {
193
194 if (curNode.getType() == TokenTypes.OBJBLOCK) {
195 curNode = curNode.getNextSibling();
196 }
197
198 if (curNode != null) {
199 final DetailAST firstTokenOnLine = result.get(curNode.getLineNo());
200
201 if (firstTokenOnLine == null
202 || firstTokenOnLine != null
203 && firstTokenOnLine.getColumnNo() >= curNode.getColumnNo())
204 {
205 result.put(curNode.getLineNo(), curNode);
206 }
207 curNode = getNextCurNode(curNode);
208 }
209 }
210 return result;
211 }
212
213
214
215
216
217
218
219 private DetailAST getNextCurNode(DetailAST curNode)
220 {
221 DetailAST nodeToVisit = curNode.getFirstChild();
222 DetailAST currentNode = curNode;
223
224 while (currentNode != null && nodeToVisit == null) {
225 nodeToVisit = currentNode.getNextSibling();
226 if (nodeToVisit == null) {
227 currentNode = currentNode.getParent();
228 }
229 }
230 return nodeToVisit;
231 }
232
233
234
235
236
237
238
239
240 private void checkAnnotationIndentation(DetailAST atNode,
241 NavigableMap<Integer, DetailAST> firstNodesOnLines)
242 {
243 final int currentIndent = atNode.getColumnNo() + indentLevel;
244 final int firstNodeIndent = atNode.getColumnNo();
245 final Collection<DetailAST> values = firstNodesOnLines.values();
246 final DetailAST lastAnnotationNode = getLastAnnotationNode(atNode);
247 final int lastAnnotationLine = lastAnnotationNode.getLineNo();
248 final int lastAnnotattionColumn = lastAnnotationNode.getColumnNo();
249
250 final Iterator<DetailAST> itr = values.iterator();
251 while (itr.hasNext() && firstNodesOnLines.size() > 1) {
252 final DetailAST node = itr.next();
253
254 if (node.getLineNo() < lastAnnotationLine
255 || node.getLineNo() == lastAnnotationLine
256 && node.getColumnNo() <= lastAnnotattionColumn)
257 {
258 final DetailAST parentNode = node.getParent();
259 if (node.getType() == TokenTypes.AT
260 && parentNode.getParent().getType() == TokenTypes.MODIFIERS)
261 {
262 logWarningMessage(node, firstNodeIndent);
263 }
264 else {
265 logWarningMessage(node, currentIndent);
266 }
267 itr.remove();
268 }
269 else {
270 break;
271 }
272 }
273 }
274
275
276
277
278
279
280 private DetailAST getLastAnnotationNode(DetailAST atNode)
281 {
282 DetailAST lastAnnotation = atNode.getParent();
283 while (lastAnnotation.getNextSibling() != null
284 && lastAnnotation.getNextSibling().getType() == TokenTypes.ANNOTATION)
285 {
286 lastAnnotation = lastAnnotation.getNextSibling();
287 }
288 return lastAnnotation.getLastChild();
289 }
290
291
292
293
294
295
296
297
298
299 private void logWarningMessage(DetailAST currentNode, int currentIndent)
300 {
301 if (forceStrictCondition) {
302 if (currentNode.getColumnNo() != currentIndent) {
303 indentCheck.indentationLog(currentNode.getLineNo(),
304 "indentation.error", currentNode.getText(),
305 currentNode.getColumnNo(), currentIndent);
306 }
307 }
308 else {
309 if (currentNode.getColumnNo() < currentIndent) {
310 indentCheck.indentationLog(currentNode.getLineNo(),
311 "indentation.error", currentNode.getText(),
312 currentNode.getColumnNo(), currentIndent);
313 }
314 }
315 }
316 }