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