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