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 * <div> 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 * </div> 041 * 042 * <p> 043 * According to 044 * <a href="https://checkstyle.org/styleguides/sun-code-conventions-19990420/CodeConventions.doc2.html#a1852"> 045 * Code Conventions for the Java Programming Language</a>, the parts of a class 046 * or interface declaration should appear in the following order: 047 * </p> 048 * <ol> 049 * <li> 050 * Class (static) variables. First the public class variables, then 051 * protected, then package level (no access modifier), and then private. 052 * </li> 053 * <li> Instance variables. First the public class variables, then 054 * protected, then package level (no access modifier), and then private. 055 * </li> 056 * <li> Constructors </li> 057 * <li> Methods </li> 058 * </ol> 059 * 060 * <p> 061 * Purpose of <b>ignore*</b> option is to ignore related violations, 062 * however it still impacts on other class members. 063 * </p> 064 * 065 * <p>ATTENTION: the check skips class fields which have 066 * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-8.3.3"> 067 * forward references </a> from validation due to the fact that we have Checkstyle's limitations 068 * to clearly detect user intention of fields location and grouping. For example: 069 * </p> 070 * <pre> 071 * public class A { 072 * private double x = 1.0; 073 * private double y = 2.0; 074 * public double slope = x / y; // will be skipped from validation due to forward reference 075 * } 076 * </pre> 077 * <ul> 078 * <li> 079 * Property {@code ignoreConstructors} - Control whether to ignore constructors. 080 * Type is {@code boolean}. 081 * Default value is {@code false}. 082 * </li> 083 * <li> 084 * Property {@code ignoreModifiers} - Control whether to ignore modifiers (fields, ...). 085 * Type is {@code boolean}. 086 * Default value is {@code false}. 087 * </li> 088 * </ul> 089 * 090 * <p> 091 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 092 * </p> 093 * 094 * <p> 095 * Violation Message Keys: 096 * </p> 097 * <ul> 098 * <li> 099 * {@code declaration.order.access} 100 * </li> 101 * <li> 102 * {@code declaration.order.constructor} 103 * </li> 104 * <li> 105 * {@code declaration.order.instance} 106 * </li> 107 * <li> 108 * {@code declaration.order.static} 109 * </li> 110 * </ul> 111 * 112 * @since 3.2 113 */ 114@FileStatefulCheck 115public class DeclarationOrderCheck extends AbstractCheck { 116 117 /** 118 * A key is pointing to the warning message text in "messages.properties" 119 * file. 120 */ 121 public static final String MSG_CONSTRUCTOR = "declaration.order.constructor"; 122 123 /** 124 * A key is pointing to the warning message text in "messages.properties" 125 * file. 126 */ 127 public static final String MSG_STATIC = "declaration.order.static"; 128 129 /** 130 * A key is pointing to the warning message text in "messages.properties" 131 * file. 132 */ 133 public static final String MSG_INSTANCE = "declaration.order.instance"; 134 135 /** 136 * A key is pointing to the warning message text in "messages.properties" 137 * file. 138 */ 139 public static final String MSG_ACCESS = "declaration.order.access"; 140 141 /** State for the VARIABLE_DEF. */ 142 private static final int STATE_STATIC_VARIABLE_DEF = 1; 143 144 /** State for the VARIABLE_DEF. */ 145 private static final int STATE_INSTANCE_VARIABLE_DEF = 2; 146 147 /** State for the CTOR_DEF. */ 148 private static final int STATE_CTOR_DEF = 3; 149 150 /** State for the METHOD_DEF. */ 151 private static final int STATE_METHOD_DEF = 4; 152 153 /** 154 * List of Declaration States. This is necessary due to 155 * inner classes that have their own state. 156 */ 157 private Deque<ScopeState> scopeStates; 158 159 /** Set of all class field names.*/ 160 private Set<String> classFieldNames; 161 162 /** Control whether to ignore constructors. */ 163 private boolean ignoreConstructors; 164 /** Control whether to ignore modifiers (fields, ...). */ 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 // nothing can be bigger than method's state 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 * Processes constructor. 235 * 236 * @param ast constructor AST. 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 * Processes modifiers. 252 * 253 * @param ast ast of Modifiers. 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 * Process if given modifiers are appropriate in given state 263 * ({@code STATE_STATIC_VARIABLE_DEF}, {@code STATE_INSTANCE_VARIABLE_DEF}, 264 * ({@code STATE_CTOR_DEF}, {@code STATE_METHOD_DEF}), if it is 265 * it updates states where appropriate or logs violation. 266 * 267 * @param modifierAst modifiers to process 268 * @param state current state 269 * @return true if modifierAst is valid in given state, false otherwise 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 * Checks if given modifiers are valid in substate of given 293 * state({@code Scope}), if it is it updates substate or else it 294 * logs violation. 295 * 296 * @param modifiersAst modifiers to process 297 * @param state current state 298 * @param isStateValid is main state for given modifiers is valid 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 * Checks whether an identifier references a field which has been already defined in class. 317 * 318 * @param fieldDef a field definition. 319 * @return true if an identifier references a field which has been already defined in class. 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 * Collects all tokens of specific type starting with the current ast node. 336 * 337 * @param ast ast node. 338 * @param tokenType token type. 339 * @return a set of all tokens of specific type starting with the current ast node. 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 * Setter to control whether to ignore constructors. 371 * 372 * @param ignoreConstructors whether to ignore constructors. 373 * @since 5.2 374 */ 375 public void setIgnoreConstructors(boolean ignoreConstructors) { 376 this.ignoreConstructors = ignoreConstructors; 377 } 378 379 /** 380 * Setter to control whether to ignore modifiers (fields, ...). 381 * 382 * @param ignoreModifiers whether to ignore modifiers. 383 * @since 5.2 384 */ 385 public void setIgnoreModifiers(boolean ignoreModifiers) { 386 this.ignoreModifiers = ignoreModifiers; 387 } 388 389 /** 390 * Private class to encapsulate the state. 391 */ 392 private static final class ScopeState { 393 394 /** The state the check is in. */ 395 private int currentScopeState = STATE_STATIC_VARIABLE_DEF; 396 397 /** The sub-state the check is in. */ 398 private Scope declarationAccess = Scope.PUBLIC; 399 400 } 401 402}