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
110
111
112
113
114
115
116
117
118
119
120
121
122 @FileStatefulCheck
123 public class DeclarationOrderCheck extends AbstractCheck {
124
125
126
127
128
129 public static final String MSG_CONSTRUCTOR = "declaration.order.constructor";
130
131
132
133
134
135 public static final String MSG_STATIC = "declaration.order.static";
136
137
138
139
140
141 public static final String MSG_INSTANCE = "declaration.order.instance";
142
143
144
145
146
147 public static final String MSG_ACCESS = "declaration.order.access";
148
149
150 private static final int STATE_STATIC_VARIABLE_DEF = 1;
151
152
153 private static final int STATE_INSTANCE_VARIABLE_DEF = 2;
154
155
156 private static final int STATE_CTOR_DEF = 3;
157
158
159 private static final int STATE_METHOD_DEF = 4;
160
161
162
163
164
165 private Deque<ScopeState> scopeStates;
166
167
168 private Set<String> classFieldNames;
169
170
171 private boolean ignoreConstructors;
172
173 private boolean ignoreModifiers;
174
175 @Override
176 public int[] getDefaultTokens() {
177 return getRequiredTokens();
178 }
179
180 @Override
181 public int[] getAcceptableTokens() {
182 return getRequiredTokens();
183 }
184
185 @Override
186 public int[] getRequiredTokens() {
187 return new int[] {
188 TokenTypes.CTOR_DEF,
189 TokenTypes.METHOD_DEF,
190 TokenTypes.MODIFIERS,
191 TokenTypes.OBJBLOCK,
192 TokenTypes.VARIABLE_DEF,
193 };
194 }
195
196 @Override
197 public void beginTree(DetailAST rootAST) {
198 scopeStates = new ArrayDeque<>();
199 classFieldNames = new HashSet<>();
200 }
201
202 @Override
203 public void visitToken(DetailAST ast) {
204 final int parentType = ast.getParent().getType();
205
206 switch (ast.getType()) {
207 case TokenTypes.OBJBLOCK:
208 scopeStates.push(new ScopeState());
209 break;
210 case TokenTypes.MODIFIERS:
211 if (parentType == TokenTypes.VARIABLE_DEF
212 && ast.getParent().getParent().getType() == TokenTypes.OBJBLOCK) {
213 processModifiers(ast);
214 }
215 break;
216 case TokenTypes.CTOR_DEF:
217 if (parentType == TokenTypes.OBJBLOCK) {
218 processConstructor(ast);
219 }
220 break;
221 case TokenTypes.METHOD_DEF:
222 if (parentType == TokenTypes.OBJBLOCK) {
223 final ScopeState state = scopeStates.peek();
224
225 state.currentScopeState = STATE_METHOD_DEF;
226 }
227 break;
228 case TokenTypes.VARIABLE_DEF:
229 if (ScopeUtil.isClassFieldDef(ast)) {
230 final DetailAST fieldDef = ast.findFirstToken(TokenTypes.IDENT);
231 classFieldNames.add(fieldDef.getText());
232 }
233 break;
234 default:
235 break;
236 }
237 }
238
239
240
241
242
243 private void processConstructor(DetailAST ast) {
244 final ScopeState state = scopeStates.peek();
245 if (state.currentScopeState > STATE_CTOR_DEF) {
246 if (!ignoreConstructors) {
247 log(ast, MSG_CONSTRUCTOR);
248 }
249 }
250 else {
251 state.currentScopeState = STATE_CTOR_DEF;
252 }
253 }
254
255
256
257
258
259 private void processModifiers(DetailAST ast) {
260 final ScopeState state = scopeStates.peek();
261 final boolean isStateValid = processModifiersState(ast, state);
262 processModifiersSubState(ast, state, isStateValid);
263 }
264
265
266
267
268
269
270
271
272
273
274 private boolean processModifiersState(DetailAST modifierAst, ScopeState state) {
275 boolean isStateValid = true;
276 if (modifierAst.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
277 if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
278 isStateValid = false;
279 log(modifierAst, MSG_INSTANCE);
280 }
281 else if (state.currentScopeState == STATE_STATIC_VARIABLE_DEF) {
282 state.declarationAccess = Scope.PUBLIC;
283 state.currentScopeState = STATE_INSTANCE_VARIABLE_DEF;
284 }
285 }
286 else {
287 if (state.currentScopeState > STATE_STATIC_VARIABLE_DEF) {
288 if (!ignoreModifiers
289 || state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
290 isStateValid = false;
291 log(modifierAst, MSG_STATIC);
292 }
293 }
294 else {
295 state.currentScopeState = STATE_STATIC_VARIABLE_DEF;
296 }
297 }
298 return isStateValid;
299 }
300
301
302
303
304
305
306
307
308
309 private void processModifiersSubState(DetailAST modifiersAst, ScopeState state,
310 boolean isStateValid) {
311 final Scope access = ScopeUtil.getScopeFromMods(modifiersAst);
312 if (state.declarationAccess.compareTo(access) > 0) {
313 if (isStateValid
314 && !ignoreModifiers
315 && !isForwardReference(modifiersAst.getParent())) {
316 log(modifiersAst, MSG_ACCESS);
317 }
318 }
319 else {
320 state.declarationAccess = access;
321 }
322 }
323
324
325
326
327
328
329 private boolean isForwardReference(DetailAST fieldDef) {
330 final DetailAST exprStartIdent = fieldDef.findFirstToken(TokenTypes.IDENT);
331 final Set<DetailAST> exprIdents = getAllTokensOfType(exprStartIdent, TokenTypes.IDENT);
332 boolean forwardReference = false;
333 for (DetailAST ident : exprIdents) {
334 if (classFieldNames.contains(ident.getText())) {
335 forwardReference = true;
336 break;
337 }
338 }
339 return forwardReference;
340 }
341
342
343
344
345
346
347
348 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
349 DetailAST vertex = ast;
350 final Set<DetailAST> result = new HashSet<>();
351 final Deque<DetailAST> stack = new ArrayDeque<>();
352 while (vertex != null || !stack.isEmpty()) {
353 if (!stack.isEmpty()) {
354 vertex = stack.pop();
355 }
356 while (vertex != null) {
357 if (vertex.getType() == tokenType && !vertex.equals(ast)) {
358 result.add(vertex);
359 }
360 if (vertex.getNextSibling() != null) {
361 stack.push(vertex.getNextSibling());
362 }
363 vertex = vertex.getFirstChild();
364 }
365 }
366 return result;
367 }
368
369 @Override
370 public void leaveToken(DetailAST ast) {
371 if (ast.getType() == TokenTypes.OBJBLOCK) {
372 scopeStates.pop();
373 }
374 }
375
376
377
378
379
380 public void setIgnoreConstructors(boolean ignoreConstructors) {
381 this.ignoreConstructors = ignoreConstructors;
382 }
383
384
385
386
387
388 public void setIgnoreModifiers(boolean ignoreModifiers) {
389 this.ignoreModifiers = ignoreModifiers;
390 }
391
392
393
394
395 private static class ScopeState {
396
397
398 private int currentScopeState = STATE_STATIC_VARIABLE_DEF;
399
400
401 private Scope declarationAccess = Scope.PUBLIC;
402
403 }
404
405 }