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.coding;
21
22 import java.util.ArrayDeque;
23 import java.util.Deque;
24 import java.util.HashSet;
25 import java.util.Set;
26
27 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
28 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
29 import com.puppycrawl.tools.checkstyle.api.DetailAST;
30 import com.puppycrawl.tools.checkstyle.api.Scope;
31 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
32 import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 @FileStatefulCheck
110 public class DeclarationOrderCheck extends AbstractCheck {
111
112
113
114
115
116 public static final String MSG_CONSTRUCTOR = "declaration.order.constructor";
117
118
119
120
121
122 public static final String MSG_STATIC = "declaration.order.static";
123
124
125
126
127
128 public static final String MSG_INSTANCE = "declaration.order.instance";
129
130
131
132
133
134 public static final String MSG_ACCESS = "declaration.order.access";
135
136
137 private static final int STATE_STATIC_VARIABLE_DEF = 1;
138
139
140 private static final int STATE_INSTANCE_VARIABLE_DEF = 2;
141
142
143 private static final int STATE_CTOR_DEF = 3;
144
145
146 private static final int STATE_METHOD_DEF = 4;
147
148
149
150
151
152 private Deque<ScopeState> scopeStates;
153
154
155 private Set<String> classFieldNames;
156
157
158 private boolean ignoreConstructors;
159
160 private boolean ignoreModifiers;
161
162 @Override
163 public int[] getDefaultTokens() {
164 return getRequiredTokens();
165 }
166
167 @Override
168 public int[] getAcceptableTokens() {
169 return getRequiredTokens();
170 }
171
172 @Override
173 public int[] getRequiredTokens() {
174 return new int[] {
175 TokenTypes.CTOR_DEF,
176 TokenTypes.METHOD_DEF,
177 TokenTypes.MODIFIERS,
178 TokenTypes.OBJBLOCK,
179 TokenTypes.VARIABLE_DEF,
180 TokenTypes.COMPACT_CTOR_DEF,
181 };
182 }
183
184 @Override
185 public void beginTree(DetailAST rootAST) {
186 scopeStates = new ArrayDeque<>();
187 classFieldNames = new HashSet<>();
188 }
189
190 @Override
191 public void visitToken(DetailAST ast) {
192 final int parentType = ast.getParent().getType();
193
194 switch (ast.getType()) {
195 case TokenTypes.OBJBLOCK:
196 scopeStates.push(new ScopeState());
197 break;
198 case TokenTypes.MODIFIERS:
199 if (parentType == TokenTypes.VARIABLE_DEF
200 && ast.getParent().getParent().getType() == TokenTypes.OBJBLOCK) {
201 processModifiers(ast);
202 }
203 break;
204 case TokenTypes.CTOR_DEF:
205 case TokenTypes.COMPACT_CTOR_DEF:
206 if (parentType == TokenTypes.OBJBLOCK) {
207 processConstructor(ast);
208 }
209 break;
210 case TokenTypes.METHOD_DEF:
211 if (parentType == TokenTypes.OBJBLOCK) {
212 final ScopeState state = scopeStates.peek();
213
214 state.currentScopeState = STATE_METHOD_DEF;
215 }
216 break;
217 case TokenTypes.VARIABLE_DEF:
218 if (ScopeUtil.isClassFieldDef(ast)) {
219 final DetailAST fieldDef = ast.findFirstToken(TokenTypes.IDENT);
220 classFieldNames.add(fieldDef.getText());
221 }
222 break;
223 default:
224 break;
225 }
226 }
227
228
229
230
231
232
233 private void processConstructor(DetailAST ast) {
234 final ScopeState state = scopeStates.peek();
235 if (state.currentScopeState > STATE_CTOR_DEF) {
236 if (!ignoreConstructors) {
237 log(ast, MSG_CONSTRUCTOR);
238 }
239 }
240 else {
241 state.currentScopeState = STATE_CTOR_DEF;
242 }
243 }
244
245
246
247
248
249
250 private void processModifiers(DetailAST ast) {
251 final ScopeState state = scopeStates.peek();
252 final boolean isStateValid = processModifiersState(ast, state);
253 processModifiersSubState(ast, state, isStateValid);
254 }
255
256
257
258
259
260
261
262
263
264
265
266 private boolean processModifiersState(DetailAST modifierAst, ScopeState state) {
267 boolean isStateValid = true;
268 if (modifierAst.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
269 if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
270 isStateValid = false;
271 log(modifierAst, MSG_INSTANCE);
272 }
273 else if (state.currentScopeState == STATE_STATIC_VARIABLE_DEF) {
274 state.declarationAccess = Scope.PUBLIC;
275 state.currentScopeState = STATE_INSTANCE_VARIABLE_DEF;
276 }
277 }
278 else if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF
279 || state.currentScopeState > STATE_STATIC_VARIABLE_DEF && !ignoreModifiers) {
280 isStateValid = false;
281 log(modifierAst, MSG_STATIC);
282 }
283 return isStateValid;
284 }
285
286
287
288
289
290
291
292
293
294
295 private void processModifiersSubState(DetailAST modifiersAst, ScopeState state,
296 boolean isStateValid) {
297 final Scope access = ScopeUtil.getScopeFromMods(modifiersAst);
298 if (state.declarationAccess.compareTo(access) > 0) {
299 if (isStateValid
300 && !ignoreModifiers
301 && !isForwardReference(modifiersAst.getParent())) {
302 log(modifiersAst, MSG_ACCESS);
303 }
304 }
305 else {
306 state.declarationAccess = access;
307 }
308 }
309
310
311
312
313
314
315
316 private boolean isForwardReference(DetailAST fieldDef) {
317 final DetailAST exprStartIdent = fieldDef.findFirstToken(TokenTypes.IDENT);
318 final Set<DetailAST> exprIdents = getAllTokensOfType(exprStartIdent, TokenTypes.IDENT);
319 boolean forwardReference = false;
320 for (DetailAST ident : exprIdents) {
321 if (classFieldNames.contains(ident.getText())) {
322 forwardReference = true;
323 break;
324 }
325 }
326 return forwardReference;
327 }
328
329
330
331
332
333
334
335
336 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
337 DetailAST vertex = ast;
338 final Set<DetailAST> result = new HashSet<>();
339 final Deque<DetailAST> stack = new ArrayDeque<>();
340 while (vertex != null || !stack.isEmpty()) {
341 if (!stack.isEmpty()) {
342 vertex = stack.pop();
343 }
344 while (vertex != null) {
345 if (vertex.getType() == tokenType && !vertex.equals(ast)) {
346 result.add(vertex);
347 }
348 if (vertex.getNextSibling() != null) {
349 stack.push(vertex.getNextSibling());
350 }
351 vertex = vertex.getFirstChild();
352 }
353 }
354 return result;
355 }
356
357 @Override
358 public void leaveToken(DetailAST ast) {
359 if (ast.getType() == TokenTypes.OBJBLOCK) {
360 scopeStates.pop();
361 }
362 }
363
364
365
366
367
368
369
370 public void setIgnoreConstructors(boolean ignoreConstructors) {
371 this.ignoreConstructors = ignoreConstructors;
372 }
373
374
375
376
377
378
379
380 public void setIgnoreModifiers(boolean ignoreModifiers) {
381 this.ignoreModifiers = ignoreModifiers;
382 }
383
384
385
386
387 private static final class ScopeState {
388
389
390 private int currentScopeState = STATE_STATIC_VARIABLE_DEF;
391
392
393 private Scope declarationAccess = Scope.PUBLIC;
394
395 }
396
397 }