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.ScopeUtils; 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 * @author r_auckenthaler 110 */ 111@FileStatefulCheck 112public class DeclarationOrderCheck extends AbstractCheck { 113 114 /** 115 * A key is pointing to the warning message text in "messages.properties" 116 * file. 117 */ 118 public static final String MSG_CONSTRUCTOR = "declaration.order.constructor"; 119 120 /** 121 * A key is pointing to the warning message text in "messages.properties" 122 * file. 123 */ 124 public static final String MSG_STATIC = "declaration.order.static"; 125 126 /** 127 * A key is pointing to the warning message text in "messages.properties" 128 * file. 129 */ 130 public static final String MSG_INSTANCE = "declaration.order.instance"; 131 132 /** 133 * A key is pointing to the warning message text in "messages.properties" 134 * file. 135 */ 136 public static final String MSG_ACCESS = "declaration.order.access"; 137 138 /** State for the VARIABLE_DEF. */ 139 private static final int STATE_STATIC_VARIABLE_DEF = 1; 140 141 /** State for the VARIABLE_DEF. */ 142 private static final int STATE_INSTANCE_VARIABLE_DEF = 2; 143 144 /** State for the CTOR_DEF. */ 145 private static final int STATE_CTOR_DEF = 3; 146 147 /** State for the METHOD_DEF. */ 148 private static final int STATE_METHOD_DEF = 4; 149 150 /** 151 * List of Declaration States. This is necessary due to 152 * inner classes that have their own state. 153 */ 154 private Deque<ScopeState> scopeStates; 155 156 /** Set of all class field names.*/ 157 private Set<String> classFieldNames; 158 159 /** If true, ignores the check to constructors. */ 160 private boolean ignoreConstructors; 161 /** If true, ignore the check to modifiers (fields, ...). */ 162 private boolean ignoreModifiers; 163 164 @Override 165 public int[] getDefaultTokens() { 166 return getRequiredTokens(); 167 } 168 169 @Override 170 public int[] getAcceptableTokens() { 171 return getRequiredTokens(); 172 } 173 174 @Override 175 public int[] getRequiredTokens() { 176 return new int[] { 177 TokenTypes.CTOR_DEF, 178 TokenTypes.METHOD_DEF, 179 TokenTypes.MODIFIERS, 180 TokenTypes.OBJBLOCK, 181 TokenTypes.VARIABLE_DEF, 182 }; 183 } 184 185 @Override 186 public void beginTree(DetailAST rootAST) { 187 scopeStates = new ArrayDeque<>(); 188 classFieldNames = new HashSet<>(); 189 } 190 191 @Override 192 public void visitToken(DetailAST ast) { 193 final int parentType = ast.getParent().getType(); 194 195 switch (ast.getType()) { 196 case TokenTypes.OBJBLOCK: 197 scopeStates.push(new ScopeState()); 198 break; 199 case TokenTypes.MODIFIERS: 200 if (parentType == TokenTypes.VARIABLE_DEF 201 && ast.getParent().getParent().getType() == TokenTypes.OBJBLOCK) { 202 processModifiers(ast); 203 } 204 break; 205 case TokenTypes.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 (ScopeUtils.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 * @param ast constructor AST. 231 */ 232 private void processConstructor(DetailAST ast) { 233 final ScopeState state = scopeStates.peek(); 234 if (state.currentScopeState > STATE_CTOR_DEF) { 235 if (!ignoreConstructors) { 236 log(ast, MSG_CONSTRUCTOR); 237 } 238 } 239 else { 240 state.currentScopeState = STATE_CTOR_DEF; 241 } 242 } 243 244 /** 245 * Processes modifiers. 246 * @param ast ast of Modifiers. 247 */ 248 private void processModifiers(DetailAST ast) { 249 final ScopeState state = scopeStates.peek(); 250 final boolean isStateValid = processModifiersState(ast, state); 251 processModifiersSubState(ast, state, isStateValid); 252 } 253 254 /** 255 * Process if given modifiers are appropriate in given state 256 * ({@code STATE_STATIC_VARIABLE_DEF}, {@code STATE_INSTANCE_VARIABLE_DEF}, 257 * ({@code STATE_CTOR_DEF}, {@code STATE_METHOD_DEF}), if it is 258 * it updates states where appropriate or logs violation. 259 * @param modifierAst modifiers to process 260 * @param state current state 261 * @return true if modifierAst is valid in given state, false otherwise 262 */ 263 private boolean processModifiersState(DetailAST modifierAst, ScopeState state) { 264 boolean isStateValid = true; 265 if (modifierAst.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 266 if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) { 267 isStateValid = false; 268 log(modifierAst, MSG_INSTANCE); 269 } 270 else if (state.currentScopeState == STATE_STATIC_VARIABLE_DEF) { 271 state.declarationAccess = Scope.PUBLIC; 272 state.currentScopeState = STATE_INSTANCE_VARIABLE_DEF; 273 } 274 } 275 else { 276 if (state.currentScopeState > STATE_STATIC_VARIABLE_DEF) { 277 if (!ignoreModifiers 278 || state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) { 279 isStateValid = false; 280 log(modifierAst, MSG_STATIC); 281 } 282 } 283 else { 284 state.currentScopeState = STATE_STATIC_VARIABLE_DEF; 285 } 286 } 287 return isStateValid; 288 } 289 290 /** 291 * Checks if given modifiers are valid in substate of given 292 * state({@code Scope}), if it is it updates substate or else it 293 * logs violation. 294 * @param modifiersAst modifiers to process 295 * @param state current state 296 * @param isStateValid is main state for given modifiers is valid 297 */ 298 private void processModifiersSubState(DetailAST modifiersAst, ScopeState state, 299 boolean isStateValid) { 300 final Scope access = ScopeUtils.getScopeFromMods(modifiersAst); 301 if (state.declarationAccess.compareTo(access) > 0) { 302 if (isStateValid 303 && !ignoreModifiers 304 && !isForwardReference(modifiersAst.getParent())) { 305 log(modifiersAst, MSG_ACCESS); 306 } 307 } 308 else { 309 state.declarationAccess = access; 310 } 311 } 312 313 /** 314 * Checks whether an identifier references a field which has been already defined in class. 315 * @param fieldDef a field definition. 316 * @return true if an identifier references a field which has been already defined in class. 317 */ 318 private boolean isForwardReference(DetailAST fieldDef) { 319 final DetailAST exprStartIdent = fieldDef.findFirstToken(TokenTypes.IDENT); 320 final Set<DetailAST> exprIdents = getAllTokensOfType(exprStartIdent, TokenTypes.IDENT); 321 boolean forwardReference = false; 322 for (DetailAST ident : exprIdents) { 323 if (classFieldNames.contains(ident.getText())) { 324 forwardReference = true; 325 break; 326 } 327 } 328 return forwardReference; 329 } 330 331 /** 332 * Collects all tokens of specific type starting with the current ast node. 333 * @param ast ast node. 334 * @param tokenType token type. 335 * @return a set of all tokens of specific type starting with the current ast node. 336 */ 337 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) { 338 DetailAST vertex = ast; 339 final Set<DetailAST> result = new HashSet<>(); 340 final Deque<DetailAST> stack = new ArrayDeque<>(); 341 while (vertex != null || !stack.isEmpty()) { 342 if (!stack.isEmpty()) { 343 vertex = stack.pop(); 344 } 345 while (vertex != null) { 346 if (vertex.getType() == tokenType && !vertex.equals(ast)) { 347 result.add(vertex); 348 } 349 if (vertex.getNextSibling() != null) { 350 stack.push(vertex.getNextSibling()); 351 } 352 vertex = vertex.getFirstChild(); 353 } 354 } 355 return result; 356 } 357 358 @Override 359 public void leaveToken(DetailAST ast) { 360 if (ast.getType() == TokenTypes.OBJBLOCK) { 361 scopeStates.pop(); 362 } 363 } 364 365 /** 366 * Sets whether to ignore constructors. 367 * @param ignoreConstructors whether to ignore constructors. 368 */ 369 public void setIgnoreConstructors(boolean ignoreConstructors) { 370 this.ignoreConstructors = ignoreConstructors; 371 } 372 373 /** 374 * Sets whether to ignore modifiers. 375 * @param ignoreModifiers whether to ignore modifiers. 376 */ 377 public void setIgnoreModifiers(boolean ignoreModifiers) { 378 this.ignoreModifiers = ignoreModifiers; 379 } 380 381 /** 382 * Private class to encapsulate the state. 383 */ 384 private static class ScopeState { 385 386 /** The state the check is in. */ 387 private int currentScopeState = STATE_STATIC_VARIABLE_DEF; 388 389 /** The sub-state the check is in. */ 390 private Scope declarationAccess = Scope.PUBLIC; 391 392 } 393 394}