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