001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 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 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 * Default value is {@code false}. 078 * </li> 079 * <li> 080 * Property {@code ignoreModifiers} - control whether to ignore modifiers (fields, ...). 081 * Default value is {@code false}. 082 * </li> 083 * </ul> 084 * <p> 085 * To configure the check: 086 * </p> 087 * <pre> 088 * <module name="DeclarationOrder"/> 089 * </pre> 090 * <p> 091 * With default options: 092 * </p> 093 * <pre> 094 * class K { 095 * int a; 096 * void m(){} 097 * K(){} <-- "Constructor definition in wrong order" 098 * int b; <-- "Instance variable definition in wrong order" 099 * } 100 * </pre> 101 * <p> 102 * With <b>ignoreConstructors</b> option: 103 * </p> 104 * <pre> 105 * class K { 106 * int a; 107 * void m(){} 108 * K(){} 109 * int b; <-- "Instance variable definition in wrong order" 110 * } 111 * </pre> 112 * <p> 113 * With <b>ignoreConstructors</b> option and without a method definition in a source class: 114 * </p> 115 * <pre> 116 * class K { 117 * int a; 118 * K(){} 119 * int b; <-- "Instance variable definition in wrong order" 120 * } 121 * </pre> 122 * 123 * @since 3.2 124 */ 125@FileStatefulCheck 126public class DeclarationOrderCheck extends AbstractCheck { 127 128 /** 129 * A key is pointing to the warning message text in "messages.properties" 130 * file. 131 */ 132 public static final String MSG_CONSTRUCTOR = "declaration.order.constructor"; 133 134 /** 135 * A key is pointing to the warning message text in "messages.properties" 136 * file. 137 */ 138 public static final String MSG_STATIC = "declaration.order.static"; 139 140 /** 141 * A key is pointing to the warning message text in "messages.properties" 142 * file. 143 */ 144 public static final String MSG_INSTANCE = "declaration.order.instance"; 145 146 /** 147 * A key is pointing to the warning message text in "messages.properties" 148 * file. 149 */ 150 public static final String MSG_ACCESS = "declaration.order.access"; 151 152 /** State for the VARIABLE_DEF. */ 153 private static final int STATE_STATIC_VARIABLE_DEF = 1; 154 155 /** State for the VARIABLE_DEF. */ 156 private static final int STATE_INSTANCE_VARIABLE_DEF = 2; 157 158 /** State for the CTOR_DEF. */ 159 private static final int STATE_CTOR_DEF = 3; 160 161 /** State for the METHOD_DEF. */ 162 private static final int STATE_METHOD_DEF = 4; 163 164 /** 165 * List of Declaration States. This is necessary due to 166 * inner classes that have their own state. 167 */ 168 private Deque<ScopeState> scopeStates; 169 170 /** Set of all class field names.*/ 171 private Set<String> classFieldNames; 172 173 /** Control whether to ignore constructors. */ 174 private boolean ignoreConstructors; 175 /** Control whether to ignore modifiers (fields, ...). */ 176 private boolean ignoreModifiers; 177 178 @Override 179 public int[] getDefaultTokens() { 180 return getRequiredTokens(); 181 } 182 183 @Override 184 public int[] getAcceptableTokens() { 185 return getRequiredTokens(); 186 } 187 188 @Override 189 public int[] getRequiredTokens() { 190 return new int[] { 191 TokenTypes.CTOR_DEF, 192 TokenTypes.METHOD_DEF, 193 TokenTypes.MODIFIERS, 194 TokenTypes.OBJBLOCK, 195 TokenTypes.VARIABLE_DEF, 196 }; 197 } 198 199 @Override 200 public void beginTree(DetailAST rootAST) { 201 scopeStates = new ArrayDeque<>(); 202 classFieldNames = new HashSet<>(); 203 } 204 205 @Override 206 public void visitToken(DetailAST ast) { 207 final int parentType = ast.getParent().getType(); 208 209 switch (ast.getType()) { 210 case TokenTypes.OBJBLOCK: 211 scopeStates.push(new ScopeState()); 212 break; 213 case TokenTypes.MODIFIERS: 214 if (parentType == TokenTypes.VARIABLE_DEF 215 && ast.getParent().getParent().getType() == TokenTypes.OBJBLOCK) { 216 processModifiers(ast); 217 } 218 break; 219 case TokenTypes.CTOR_DEF: 220 if (parentType == TokenTypes.OBJBLOCK) { 221 processConstructor(ast); 222 } 223 break; 224 case TokenTypes.METHOD_DEF: 225 if (parentType == TokenTypes.OBJBLOCK) { 226 final ScopeState state = scopeStates.peek(); 227 // nothing can be bigger than method's state 228 state.currentScopeState = STATE_METHOD_DEF; 229 } 230 break; 231 case TokenTypes.VARIABLE_DEF: 232 if (ScopeUtil.isClassFieldDef(ast)) { 233 final DetailAST fieldDef = ast.findFirstToken(TokenTypes.IDENT); 234 classFieldNames.add(fieldDef.getText()); 235 } 236 break; 237 default: 238 break; 239 } 240 } 241 242 /** 243 * Processes constructor. 244 * 245 * @param ast constructor AST. 246 */ 247 private void processConstructor(DetailAST ast) { 248 final ScopeState state = scopeStates.peek(); 249 if (state.currentScopeState > STATE_CTOR_DEF) { 250 if (!ignoreConstructors) { 251 log(ast, MSG_CONSTRUCTOR); 252 } 253 } 254 else { 255 state.currentScopeState = STATE_CTOR_DEF; 256 } 257 } 258 259 /** 260 * Processes modifiers. 261 * 262 * @param ast ast of Modifiers. 263 */ 264 private void processModifiers(DetailAST ast) { 265 final ScopeState state = scopeStates.peek(); 266 final boolean isStateValid = processModifiersState(ast, state); 267 processModifiersSubState(ast, state, isStateValid); 268 } 269 270 /** 271 * Process if given modifiers are appropriate in given state 272 * ({@code STATE_STATIC_VARIABLE_DEF}, {@code STATE_INSTANCE_VARIABLE_DEF}, 273 * ({@code STATE_CTOR_DEF}, {@code STATE_METHOD_DEF}), if it is 274 * it updates states where appropriate or logs violation. 275 * 276 * @param modifierAst modifiers to process 277 * @param state current state 278 * @return true if modifierAst is valid in given state, false otherwise 279 */ 280 private boolean processModifiersState(DetailAST modifierAst, ScopeState state) { 281 boolean isStateValid = true; 282 if (modifierAst.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 283 if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) { 284 isStateValid = false; 285 log(modifierAst, MSG_INSTANCE); 286 } 287 else if (state.currentScopeState == STATE_STATIC_VARIABLE_DEF) { 288 state.declarationAccess = Scope.PUBLIC; 289 state.currentScopeState = STATE_INSTANCE_VARIABLE_DEF; 290 } 291 } 292 else { 293 if (state.currentScopeState > STATE_STATIC_VARIABLE_DEF) { 294 if (!ignoreModifiers 295 || state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) { 296 isStateValid = false; 297 log(modifierAst, MSG_STATIC); 298 } 299 } 300 else { 301 state.currentScopeState = STATE_STATIC_VARIABLE_DEF; 302 } 303 } 304 return isStateValid; 305 } 306 307 /** 308 * Checks if given modifiers are valid in substate of given 309 * state({@code Scope}), if it is it updates substate or else it 310 * logs violation. 311 * 312 * @param modifiersAst modifiers to process 313 * @param state current state 314 * @param isStateValid is main state for given modifiers is valid 315 */ 316 private void processModifiersSubState(DetailAST modifiersAst, ScopeState state, 317 boolean isStateValid) { 318 final Scope access = ScopeUtil.getScopeFromMods(modifiersAst); 319 if (state.declarationAccess.compareTo(access) > 0) { 320 if (isStateValid 321 && !ignoreModifiers 322 && !isForwardReference(modifiersAst.getParent())) { 323 log(modifiersAst, MSG_ACCESS); 324 } 325 } 326 else { 327 state.declarationAccess = access; 328 } 329 } 330 331 /** 332 * Checks whether an identifier references a field which has been already defined in class. 333 * 334 * @param fieldDef a field definition. 335 * @return true if an identifier references a field which has been already defined in class. 336 */ 337 private boolean isForwardReference(DetailAST fieldDef) { 338 final DetailAST exprStartIdent = fieldDef.findFirstToken(TokenTypes.IDENT); 339 final Set<DetailAST> exprIdents = getAllTokensOfType(exprStartIdent, TokenTypes.IDENT); 340 boolean forwardReference = false; 341 for (DetailAST ident : exprIdents) { 342 if (classFieldNames.contains(ident.getText())) { 343 forwardReference = true; 344 break; 345 } 346 } 347 return forwardReference; 348 } 349 350 /** 351 * Collects all tokens of specific type starting with the current ast node. 352 * 353 * @param ast ast node. 354 * @param tokenType token type. 355 * @return a set of all tokens of specific type starting with the current ast node. 356 */ 357 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) { 358 DetailAST vertex = ast; 359 final Set<DetailAST> result = new HashSet<>(); 360 final Deque<DetailAST> stack = new ArrayDeque<>(); 361 while (vertex != null || !stack.isEmpty()) { 362 if (!stack.isEmpty()) { 363 vertex = stack.pop(); 364 } 365 while (vertex != null) { 366 if (vertex.getType() == tokenType && !vertex.equals(ast)) { 367 result.add(vertex); 368 } 369 if (vertex.getNextSibling() != null) { 370 stack.push(vertex.getNextSibling()); 371 } 372 vertex = vertex.getFirstChild(); 373 } 374 } 375 return result; 376 } 377 378 @Override 379 public void leaveToken(DetailAST ast) { 380 if (ast.getType() == TokenTypes.OBJBLOCK) { 381 scopeStates.pop(); 382 } 383 } 384 385 /** 386 * Setter to control whether to ignore constructors. 387 * 388 * @param ignoreConstructors whether to ignore constructors. 389 */ 390 public void setIgnoreConstructors(boolean ignoreConstructors) { 391 this.ignoreConstructors = ignoreConstructors; 392 } 393 394 /** 395 * Setter to control whether to ignore modifiers (fields, ...). 396 * 397 * @param ignoreModifiers whether to ignore modifiers. 398 */ 399 public void setIgnoreModifiers(boolean ignoreModifiers) { 400 this.ignoreModifiers = ignoreModifiers; 401 } 402 403 /** 404 * Private class to encapsulate the state. 405 */ 406 private static class ScopeState { 407 408 /** The state the check is in. */ 409 private int currentScopeState = STATE_STATIC_VARIABLE_DEF; 410 411 /** The sub-state the check is in. */ 412 private Scope declarationAccess = Scope.PUBLIC; 413 414 } 415 416}