001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.coding; 021 022import java.util.ArrayDeque; 023import java.util.Deque; 024import java.util.HashSet; 025import java.util.Set; 026 027import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 028import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 029import com.puppycrawl.tools.checkstyle.api.DetailAST; 030import com.puppycrawl.tools.checkstyle.api.Scope; 031import com.puppycrawl.tools.checkstyle.api.TokenTypes; 032import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 033 034/** 035 * <p> 036 * Checks that the parts of a class, record, or interface declaration appear in the order 037 * suggested by the 038 * <a href="https://checkstyle.org/styleguides/sun-code-conventions-19990420/CodeConventions.doc2.html#a1852"> 039 * Code Conventions for the Java Programming Language</a>. 040 * </p> 041 * <p> 042 * According to 043 * <a href="https://checkstyle.org/styleguides/sun-code-conventions-19990420/CodeConventions.doc2.html#a1852"> 044 * Code Conventions for the Java Programming Language</a>, the parts of a class 045 * or interface declaration should appear in the following order: 046 * </p> 047 * <ol> 048 * <li> 049 * Class (static) variables. First the public class variables, then 050 * protected, then package level (no access modifier), and then private. 051 * </li> 052 * <li> Instance variables. First the public class variables, then 053 * protected, then package level (no access modifier), and then private. 054 * </li> 055 * <li> Constructors </li> 056 * <li> Methods </li> 057 * </ol> 058 * <p> 059 * Purpose of <b>ignore*</b> option is to ignore related violations, 060 * however it still impacts on other class members. 061 * </p> 062 * <p>ATTENTION: the check skips class fields which have 063 * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-8.3.3"> 064 * forward references </a> from validation due to the fact that we have Checkstyle's limitations 065 * to clearly detect user intention of fields location and grouping. For example: 066 * </p> 067 * <pre> 068 * public class A { 069 * private double x = 1.0; 070 * private double y = 2.0; 071 * public double slope = x / y; // will be skipped from validation due to forward reference 072 * } 073 * </pre> 074 * <ul> 075 * <li> 076 * Property {@code ignoreConstructors} - Control whether to ignore constructors. 077 * Type is {@code boolean}. 078 * Default value is {@code false}. 079 * </li> 080 * <li> 081 * Property {@code ignoreModifiers} - Control whether to ignore modifiers (fields, ...). 082 * Type is {@code boolean}. 083 * Default value is {@code false}. 084 * </li> 085 * </ul> 086 * <p> 087 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 088 * </p> 089 * <p> 090 * Violation Message Keys: 091 * </p> 092 * <ul> 093 * <li> 094 * {@code declaration.order.access} 095 * </li> 096 * <li> 097 * {@code declaration.order.constructor} 098 * </li> 099 * <li> 100 * {@code declaration.order.instance} 101 * </li> 102 * <li> 103 * {@code declaration.order.static} 104 * </li> 105 * </ul> 106 * 107 * @since 3.2 108 */ 109@FileStatefulCheck 110public class DeclarationOrderCheck extends AbstractCheck { 111 112 /** 113 * A key is pointing to the warning message text in "messages.properties" 114 * file. 115 */ 116 public static final String MSG_CONSTRUCTOR = "declaration.order.constructor"; 117 118 /** 119 * A key is pointing to the warning message text in "messages.properties" 120 * file. 121 */ 122 public static final String MSG_STATIC = "declaration.order.static"; 123 124 /** 125 * A key is pointing to the warning message text in "messages.properties" 126 * file. 127 */ 128 public static final String MSG_INSTANCE = "declaration.order.instance"; 129 130 /** 131 * A key is pointing to the warning message text in "messages.properties" 132 * file. 133 */ 134 public static final String MSG_ACCESS = "declaration.order.access"; 135 136 /** State for the VARIABLE_DEF. */ 137 private static final int STATE_STATIC_VARIABLE_DEF = 1; 138 139 /** State for the VARIABLE_DEF. */ 140 private static final int STATE_INSTANCE_VARIABLE_DEF = 2; 141 142 /** State for the CTOR_DEF. */ 143 private static final int STATE_CTOR_DEF = 3; 144 145 /** State for the METHOD_DEF. */ 146 private static final int STATE_METHOD_DEF = 4; 147 148 /** 149 * List of Declaration States. This is necessary due to 150 * inner classes that have their own state. 151 */ 152 private Deque<ScopeState> scopeStates; 153 154 /** Set of all class field names.*/ 155 private Set<String> classFieldNames; 156 157 /** Control whether to ignore constructors. */ 158 private boolean ignoreConstructors; 159 /** Control whether to ignore modifiers (fields, ...). */ 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 // nothing can be bigger than method's state 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 * Processes constructor. 230 * 231 * @param ast constructor AST. 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 * Processes modifiers. 247 * 248 * @param ast ast of Modifiers. 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 * Process if given modifiers are appropriate in given state 258 * ({@code STATE_STATIC_VARIABLE_DEF}, {@code STATE_INSTANCE_VARIABLE_DEF}, 259 * ({@code STATE_CTOR_DEF}, {@code STATE_METHOD_DEF}), if it is 260 * it updates states where appropriate or logs violation. 261 * 262 * @param modifierAst modifiers to process 263 * @param state current state 264 * @return true if modifierAst is valid in given state, false otherwise 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 * Checks if given modifiers are valid in substate of given 288 * state({@code Scope}), if it is it updates substate or else it 289 * logs violation. 290 * 291 * @param modifiersAst modifiers to process 292 * @param state current state 293 * @param isStateValid is main state for given modifiers is valid 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 * Checks whether an identifier references a field which has been already defined in class. 312 * 313 * @param fieldDef a field definition. 314 * @return true if an identifier references a field which has been already defined in class. 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 * Collects all tokens of specific type starting with the current ast node. 331 * 332 * @param ast ast node. 333 * @param tokenType token type. 334 * @return a set of all tokens of specific type starting with the current ast node. 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 * Setter to control whether to ignore constructors. 366 * 367 * @param ignoreConstructors whether to ignore constructors. 368 * @since 5.2 369 */ 370 public void setIgnoreConstructors(boolean ignoreConstructors) { 371 this.ignoreConstructors = ignoreConstructors; 372 } 373 374 /** 375 * Setter to control whether to ignore modifiers (fields, ...). 376 * 377 * @param ignoreModifiers whether to ignore modifiers. 378 * @since 5.2 379 */ 380 public void setIgnoreModifiers(boolean ignoreModifiers) { 381 this.ignoreModifiers = ignoreModifiers; 382 } 383 384 /** 385 * Private class to encapsulate the state. 386 */ 387 private static final class ScopeState { 388 389 /** The state the check is in. */ 390 private int currentScopeState = STATE_STATIC_VARIABLE_DEF; 391 392 /** The sub-state the check is in. */ 393 private Scope declarationAccess = Scope.PUBLIC; 394 395 } 396 397}