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