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