Coverage Report - com.puppycrawl.tools.checkstyle.checks.coding.DeclarationOrderCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
DeclarationOrderCheck
100%
86/86
100%
60/60
3.286
DeclarationOrderCheck$1
N/A
N/A
3.286
DeclarationOrderCheck$ScopeState
100%
3/3
N/A
3.286
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2017 the original author or authors.
 4  
 //
 5  
 // This library is free software; you can redistribute it and/or
 6  
 // modify it under the terms of the GNU Lesser General Public
 7  
 // License as published by the Free Software Foundation; either
 8  
 // version 2.1 of the License, or (at your option) any later version.
 9  
 //
 10  
 // This library is distributed in the hope that it will be useful,
 11  
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 // Lesser General Public License for more details.
 14  
 //
 15  
 // You should have received a copy of the GNU Lesser General Public
 16  
 // License along with this library; if not, write to the Free Software
 17  
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 18  
 ////////////////////////////////////////////////////////////////////////////////
 19  
 
 20  
 package com.puppycrawl.tools.checkstyle.checks.coding;
 21  
 
 22  
 import java.util.ArrayDeque;
 23  
 import java.util.Deque;
 24  
 import java.util.HashSet;
 25  
 import java.util.Set;
 26  
 
 27  
 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
 28  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 29  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 30  
 import com.puppycrawl.tools.checkstyle.api.Scope;
 31  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 32  
 import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
 33  
 
 34  
 /**
 35  
  * Checks that the parts of a class or interface declaration
 36  
  * appear in the order suggested by the
 37  
  * <a href=
 38  
  * "http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-141855.html#1852">
 39  
  * Code Conventions for the Java Programming Language</a>.
 40  
  *
 41  
  *
 42  
  * <ol>
 43  
  * <li> Class (static) variables. First the public class variables, then
 44  
  *      the protected, then package level (no access modifier), and then
 45  
  *      the private. </li>
 46  
  * <li> Instance variables. First the public class variables, then
 47  
  *      the protected, then package level (no access modifier), and then
 48  
  *      the private. </li>
 49  
  * <li> Constructors </li>
 50  
  * <li> Methods </li>
 51  
  * </ol>
 52  
  *
 53  
  * <p>ATTENTION: the check skips class fields which have
 54  
  * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.3.3">
 55  
  * forward references </a> from validation due to the fact that we have Checkstyle's limitations
 56  
  * to clearly detect user intention of fields location and grouping. For example,
 57  
  * <pre>{@code
 58  
  *      public class A {
 59  
  *          private double x = 1.0;
 60  
  *          private double y = 2.0;
 61  
  *          public double slope = x / y; // will be skipped from validation due to forward reference
 62  
  *      }
 63  
  * }</pre>
 64  
  *
 65  
  * <p>Available options:
 66  
  * <ul>
 67  
  * <li>ignoreModifiers</li>
 68  
  * <li>ignoreConstructors</li>
 69  
  * </ul>
 70  
  *
 71  
  * <p>Purpose of <b>ignore*</b> option is to ignore related violations,
 72  
  * however it still impacts on other class members.
 73  
  *
 74  
  * <p>For example:
 75  
  * <pre>{@code
 76  
  *     class K {
 77  
  *         int a;
 78  
  *         void m(){}
 79  
  *         K(){}  &lt;-- "Constructor definition in wrong order"
 80  
  *         int b; &lt;-- "Instance variable definition in wrong order"
 81  
  *     }
 82  
  * }</pre>
 83  
  *
 84  
  * <p>With <b>ignoreConstructors</b> option:
 85  
  * <pre>{@code
 86  
  *     class K {
 87  
  *         int a;
 88  
  *         void m(){}
 89  
  *         K(){}
 90  
  *         int b; &lt;-- "Instance variable definition in wrong order"
 91  
  *     }
 92  
  * }</pre>
 93  
  *
 94  
  * <p>With <b>ignoreConstructors</b> option and without a method definition in a source class:
 95  
  * <pre>{@code
 96  
  *     class K {
 97  
  *         int a;
 98  
  *         K(){}
 99  
  *         int b; &lt;-- "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  
  * &lt;module name="DeclarationOrder"/&gt;
 107  
  * </pre>
 108  
  *
 109  
  * @author r_auckenthaler
 110  
  */
 111  
 @FileStatefulCheck
 112  15
 public 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  19
         return getRequiredTokens();
 167  
     }
 168  
 
 169  
     @Override
 170  
     public int[] getAcceptableTokens() {
 171  5
         return getRequiredTokens();
 172  
     }
 173  
 
 174  
     @Override
 175  
     public int[] getRequiredTokens() {
 176  43
         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  6
         scopeStates = new ArrayDeque<>();
 188  6
         classFieldNames = new HashSet<>();
 189  6
     }
 190  
 
 191  
     @Override
 192  
     public void visitToken(DetailAST ast) {
 193  409
         final int parentType = ast.getParent().getType();
 194  
 
 195  409
         switch (ast.getType()) {
 196  
             case TokenTypes.OBJBLOCK:
 197  20
                 scopeStates.push(new ScopeState());
 198  20
                 break;
 199  
             case TokenTypes.MODIFIERS:
 200  203
                 if (parentType == TokenTypes.VARIABLE_DEF
 201  138
                     && ast.getParent().getParent().getType() == TokenTypes.OBJBLOCK) {
 202  116
                     processModifiers(ast);
 203  
                 }
 204  
                 break;
 205  
             case TokenTypes.CTOR_DEF:
 206  16
                 if (parentType == TokenTypes.OBJBLOCK) {
 207  15
                     processConstructor(ast);
 208  
                 }
 209  
                 break;
 210  
             case TokenTypes.METHOD_DEF:
 211  31
                 if (parentType == TokenTypes.OBJBLOCK) {
 212  30
                     final ScopeState state = scopeStates.peek();
 213  
                     // nothing can be bigger than method's state
 214  30
                     state.currentScopeState = STATE_METHOD_DEF;
 215  30
                 }
 216  
                 break;
 217  
             case TokenTypes.VARIABLE_DEF:
 218  138
                 if (ScopeUtils.isClassFieldDef(ast)) {
 219  116
                     final DetailAST fieldDef = ast.findFirstToken(TokenTypes.IDENT);
 220  116
                     classFieldNames.add(fieldDef.getText());
 221  116
                 }
 222  
                 break;
 223  
             default:
 224  
                 break;
 225  
         }
 226  409
     }
 227  
 
 228  
     /**
 229  
      * Processes constructor.
 230  
      * @param ast constructor AST.
 231  
      */
 232  
     private void processConstructor(DetailAST ast) {
 233  
 
 234  15
         final ScopeState state = scopeStates.peek();
 235  15
         if (state.currentScopeState > STATE_CTOR_DEF) {
 236  6
             if (!ignoreConstructors) {
 237  4
                 log(ast, MSG_CONSTRUCTOR);
 238  
             }
 239  
         }
 240  
         else {
 241  9
             state.currentScopeState = STATE_CTOR_DEF;
 242  
         }
 243  15
     }
 244  
 
 245  
     /**
 246  
      * Processes modifiers.
 247  
      * @param ast ast of Modifiers.
 248  
      */
 249  
     private void processModifiers(DetailAST ast) {
 250  116
         final ScopeState state = scopeStates.peek();
 251  116
         final boolean isStateValid = processModifiersState(ast, state);
 252  116
         processModifiersSubState(ast, state, isStateValid);
 253  116
     }
 254  
 
 255  
     /**
 256  
      * Process if given modifiers are appropriate in given state
 257  
      * ({@code STATE_STATIC_VARIABLE_DEF}, {@code STATE_INSTANCE_VARIABLE_DEF},
 258  
      * ({@code STATE_CTOR_DEF}, {@code STATE_METHOD_DEF}), if it is
 259  
      * it updates states where appropriate or logs violation.
 260  
      * @param modifierAst modifiers to process
 261  
      * @param state current state
 262  
      * @return true if modifierAst is valid in given state, false otherwise
 263  
      */
 264  
     private boolean processModifiersState(DetailAST modifierAst, ScopeState state) {
 265  116
         boolean isStateValid = true;
 266  116
         if (modifierAst.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
 267  21
             if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
 268  6
                 isStateValid = false;
 269  6
                 log(modifierAst, MSG_INSTANCE);
 270  
             }
 271  15
             else if (state.currentScopeState == STATE_STATIC_VARIABLE_DEF) {
 272  11
                 state.declarationAccess = Scope.PUBLIC;
 273  11
                 state.currentScopeState = STATE_INSTANCE_VARIABLE_DEF;
 274  
             }
 275  
         }
 276  
         else {
 277  95
             if (state.currentScopeState > STATE_STATIC_VARIABLE_DEF) {
 278  17
                 if (!ignoreModifiers
 279  5
                         || state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
 280  15
                     isStateValid = false;
 281  15
                     log(modifierAst, MSG_STATIC);
 282  
                 }
 283  
             }
 284  
             else {
 285  78
                 state.currentScopeState = STATE_STATIC_VARIABLE_DEF;
 286  
             }
 287  
         }
 288  116
         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  
      * @param modifiersAst modifiers to process
 296  
      * @param state current state
 297  
      * @param isStateValid is main state for given modifiers is valid
 298  
      */
 299  
     private void processModifiersSubState(DetailAST modifiersAst, ScopeState state,
 300  
                                           boolean isStateValid) {
 301  116
         final Scope access = ScopeUtils.getScopeFromMods(modifiersAst);
 302  116
         if (state.declarationAccess.compareTo(access) > 0) {
 303  67
             if (isStateValid
 304  
                     && !ignoreModifiers
 305  38
                     && !isForwardReference(modifiersAst.getParent())) {
 306  31
                 log(modifiersAst, MSG_ACCESS);
 307  
             }
 308  
         }
 309  
         else {
 310  49
             state.declarationAccess = access;
 311  
         }
 312  116
     }
 313  
 
 314  
     /**
 315  
      * Checks whether an identifier references a field which has been already defined in class.
 316  
      * @param fieldDef a field definition.
 317  
      * @return true if an identifier references a field which has been already defined in class.
 318  
      */
 319  
     private boolean isForwardReference(DetailAST fieldDef) {
 320  38
         final DetailAST exprStartIdent = fieldDef.findFirstToken(TokenTypes.IDENT);
 321  38
         final Set<DetailAST> exprIdents = getAllTokensOfType(exprStartIdent, TokenTypes.IDENT);
 322  38
         boolean forwardReference = false;
 323  38
         for (DetailAST ident : exprIdents) {
 324  11
             if (classFieldNames.contains(ident.getText())) {
 325  7
                 forwardReference = true;
 326  7
                 break;
 327  
             }
 328  4
         }
 329  38
         return forwardReference;
 330  
     }
 331  
 
 332  
     /**
 333  
      * Collects all tokens of specific type starting with the current ast node.
 334  
      * @param ast ast node.
 335  
      * @param tokenType token type.
 336  
      * @return a set of all tokens of specific type starting with the current ast node.
 337  
      */
 338  
     private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
 339  38
         DetailAST vertex = ast;
 340  38
         final Set<DetailAST> result = new HashSet<>();
 341  38
         final Deque<DetailAST> stack = new ArrayDeque<>();
 342  162
         while (vertex != null || !stack.isEmpty()) {
 343  124
             if (!stack.isEmpty()) {
 344  86
                 vertex = stack.pop();
 345  
             }
 346  334
             while (vertex != null) {
 347  210
                 if (vertex.getType() == tokenType && !vertex.equals(ast)) {
 348  14
                     result.add(vertex);
 349  
                 }
 350  210
                 if (vertex.getNextSibling() != null) {
 351  86
                     stack.push(vertex.getNextSibling());
 352  
                 }
 353  210
                 vertex = vertex.getFirstChild();
 354  
             }
 355  
         }
 356  38
         return result;
 357  
     }
 358  
 
 359  
     @Override
 360  
     public void leaveToken(DetailAST ast) {
 361  406
         if (ast.getType() == TokenTypes.OBJBLOCK) {
 362  20
             scopeStates.pop();
 363  
         }
 364  406
     }
 365  
 
 366  
     /**
 367  
      * Sets whether to ignore constructors.
 368  
      * @param ignoreConstructors whether to ignore constructors.
 369  
      */
 370  
     public void setIgnoreConstructors(boolean ignoreConstructors) {
 371  2
         this.ignoreConstructors = ignoreConstructors;
 372  2
     }
 373  
 
 374  
     /**
 375  
      * Sets whether to ignore modifiers.
 376  
      * @param ignoreModifiers whether to ignore modifiers.
 377  
      */
 378  
     public void setIgnoreModifiers(boolean ignoreModifiers) {
 379  2
         this.ignoreModifiers = ignoreModifiers;
 380  2
     }
 381  
 
 382  
     /**
 383  
      * Private class to encapsulate the state.
 384  
      */
 385  495
     private static class ScopeState {
 386  
         /** The state the check is in. */
 387  20
         private int currentScopeState = STATE_STATIC_VARIABLE_DEF;
 388  
 
 389  
         /** The sub-state the check is in. */
 390  20
         private Scope declarationAccess = Scope.PUBLIC;
 391  
     }
 392  
 }