Coverage Report - com.puppycrawl.tools.checkstyle.checks.metrics.JavaNCSSCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
JavaNCSSCheck
100%
65/65
100%
46/46
0
JavaNCSSCheck$1
N/A
N/A
0
JavaNCSSCheck$Counter
100%
4/4
N/A
0
 
 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.metrics;
 21  
 
 22  
 import java.util.ArrayDeque;
 23  
 import java.util.Deque;
 24  
 
 25  
 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
 26  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 27  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 28  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 29  
 
 30  
 /**
 31  
  * This check calculates the Non Commenting Source Statements (NCSS) metric for
 32  
  * java source files and methods. The check adheres to the <a
 33  
  * href="http://www.kclee.com/clemens/java/javancss">JavaNCSS specification
 34  
  * </a> and gives the same results as the JavaNCSS tool.
 35  
  *
 36  
  * <p>The NCSS-metric tries to determine complexity of methods, classes and files
 37  
  * by counting the non commenting lines. Roughly said this is (nearly)
 38  
  * equivalent to counting the semicolons and opening curly braces.
 39  
  *
 40  
  * @author Lars K√∂dderitzsch
 41  
  */
 42  
 // -@cs[AbbreviationAsWordInName] We can not change it as,
 43  
 // check's name is a part of API (used in configurations).
 44  
 @FileStatefulCheck
 45  14
 public class JavaNCSSCheck extends AbstractCheck {
 46  
 
 47  
     /**
 48  
      * A key is pointing to the warning message text in "messages.properties"
 49  
      * file.
 50  
      */
 51  
     public static final String MSG_METHOD = "ncss.method";
 52  
 
 53  
     /**
 54  
      * A key is pointing to the warning message text in "messages.properties"
 55  
      * file.
 56  
      */
 57  
     public static final String MSG_CLASS = "ncss.class";
 58  
 
 59  
     /**
 60  
      * A key is pointing to the warning message text in "messages.properties"
 61  
      * file.
 62  
      */
 63  
     public static final String MSG_FILE = "ncss.file";
 64  
 
 65  
     /** Default constant for max file ncss. */
 66  
     private static final int FILE_MAX_NCSS = 2000;
 67  
 
 68  
     /** Default constant for max file ncss. */
 69  
     private static final int CLASS_MAX_NCSS = 1500;
 70  
 
 71  
     /** Default constant for max method ncss. */
 72  
     private static final int METHOD_MAX_NCSS = 50;
 73  
 
 74  
     /** Maximum ncss for a complete source file. */
 75  14
     private int fileMaximum = FILE_MAX_NCSS;
 76  
 
 77  
     /** Maximum ncss for a class. */
 78  14
     private int classMaximum = CLASS_MAX_NCSS;
 79  
 
 80  
     /** Maximum ncss for a method. */
 81  14
     private int methodMaximum = METHOD_MAX_NCSS;
 82  
 
 83  
     /** List containing the stacked counters. */
 84  
     private Deque<Counter> counters;
 85  
 
 86  
     @Override
 87  
     public int[] getDefaultTokens() {
 88  18
         return getRequiredTokens();
 89  
     }
 90  
 
 91  
     @Override
 92  
     public int[] getRequiredTokens() {
 93  42
         return new int[] {
 94  
             TokenTypes.CLASS_DEF,
 95  
             TokenTypes.INTERFACE_DEF,
 96  
             TokenTypes.METHOD_DEF,
 97  
             TokenTypes.CTOR_DEF,
 98  
             TokenTypes.INSTANCE_INIT,
 99  
             TokenTypes.STATIC_INIT,
 100  
             TokenTypes.PACKAGE_DEF,
 101  
             TokenTypes.IMPORT,
 102  
             TokenTypes.VARIABLE_DEF,
 103  
             TokenTypes.CTOR_CALL,
 104  
             TokenTypes.SUPER_CTOR_CALL,
 105  
             TokenTypes.LITERAL_IF,
 106  
             TokenTypes.LITERAL_ELSE,
 107  
             TokenTypes.LITERAL_WHILE,
 108  
             TokenTypes.LITERAL_DO,
 109  
             TokenTypes.LITERAL_FOR,
 110  
             TokenTypes.LITERAL_SWITCH,
 111  
             TokenTypes.LITERAL_BREAK,
 112  
             TokenTypes.LITERAL_CONTINUE,
 113  
             TokenTypes.LITERAL_RETURN,
 114  
             TokenTypes.LITERAL_THROW,
 115  
             TokenTypes.LITERAL_SYNCHRONIZED,
 116  
             TokenTypes.LITERAL_CATCH,
 117  
             TokenTypes.LITERAL_FINALLY,
 118  
             TokenTypes.EXPR,
 119  
             TokenTypes.LABELED_STAT,
 120  
             TokenTypes.LITERAL_CASE,
 121  
             TokenTypes.LITERAL_DEFAULT,
 122  
         };
 123  
     }
 124  
 
 125  
     @Override
 126  
     public int[] getAcceptableTokens() {
 127  5
         return getRequiredTokens();
 128  
     }
 129  
 
 130  
     @Override
 131  
     public void beginTree(DetailAST rootAST) {
 132  4
         counters = new ArrayDeque<>();
 133  
 
 134  
         //add a counter for the file
 135  4
         counters.push(new Counter());
 136  4
     }
 137  
 
 138  
     @Override
 139  
     public void visitToken(DetailAST ast) {
 140  178
         final int tokenType = ast.getType();
 141  
 
 142  178
         if (tokenType == TokenTypes.CLASS_DEF
 143  
             || tokenType == TokenTypes.METHOD_DEF
 144  
             || tokenType == TokenTypes.CTOR_DEF
 145  
             || tokenType == TokenTypes.STATIC_INIT
 146  
             || tokenType == TokenTypes.INSTANCE_INIT) {
 147  
             //add a counter for this class/method
 148  36
             counters.push(new Counter());
 149  
         }
 150  
 
 151  
         //check if token is countable
 152  178
         if (isCountable(ast)) {
 153  
             //increment the stacked counters
 154  118
             counters.forEach(Counter::increment);
 155  
         }
 156  178
     }
 157  
 
 158  
     @Override
 159  
     public void leaveToken(DetailAST ast) {
 160  178
         final int tokenType = ast.getType();
 161  178
         if (tokenType == TokenTypes.METHOD_DEF
 162  
             || tokenType == TokenTypes.CTOR_DEF
 163  
             || tokenType == TokenTypes.STATIC_INIT
 164  
             || tokenType == TokenTypes.INSTANCE_INIT) {
 165  
             //pop counter from the stack
 166  24
             final Counter counter = counters.pop();
 167  
 
 168  24
             final int count = counter.getCount();
 169  24
             if (count > methodMaximum) {
 170  16
                 log(ast.getLineNo(), ast.getColumnNo(), MSG_METHOD,
 171  8
                         count, methodMaximum);
 172  
             }
 173  24
         }
 174  154
         else if (tokenType == TokenTypes.CLASS_DEF) {
 175  
             //pop counter from the stack
 176  12
             final Counter counter = counters.pop();
 177  
 
 178  12
             final int count = counter.getCount();
 179  12
             if (count > classMaximum) {
 180  8
                 log(ast.getLineNo(), ast.getColumnNo(), MSG_CLASS,
 181  4
                         count, classMaximum);
 182  
             }
 183  
         }
 184  178
     }
 185  
 
 186  
     @Override
 187  
     public void finishTree(DetailAST rootAST) {
 188  
         //pop counter from the stack
 189  4
         final Counter counter = counters.pop();
 190  
 
 191  4
         final int count = counter.getCount();
 192  4
         if (count > fileMaximum) {
 193  2
             log(rootAST.getLineNo(), rootAST.getColumnNo(), MSG_FILE,
 194  1
                     count, fileMaximum);
 195  
         }
 196  4
     }
 197  
 
 198  
     /**
 199  
      * Sets the maximum ncss for a file.
 200  
      *
 201  
      * @param fileMaximum
 202  
      *            the maximum ncss
 203  
      */
 204  
     public void setFileMaximum(int fileMaximum) {
 205  2
         this.fileMaximum = fileMaximum;
 206  2
     }
 207  
 
 208  
     /**
 209  
      * Sets the maximum ncss for a class.
 210  
      *
 211  
      * @param classMaximum
 212  
      *            the maximum ncss
 213  
      */
 214  
     public void setClassMaximum(int classMaximum) {
 215  2
         this.classMaximum = classMaximum;
 216  2
     }
 217  
 
 218  
     /**
 219  
      * Sets the maximum ncss for a method.
 220  
      *
 221  
      * @param methodMaximum
 222  
      *            the maximum ncss
 223  
      */
 224  
     public void setMethodMaximum(int methodMaximum) {
 225  3
         this.methodMaximum = methodMaximum;
 226  3
     }
 227  
 
 228  
     /**
 229  
      * Checks if a token is countable for the ncss metric.
 230  
      *
 231  
      * @param ast
 232  
      *            the AST
 233  
      * @return true if the token is countable
 234  
      */
 235  
     private static boolean isCountable(DetailAST ast) {
 236  178
         boolean countable = true;
 237  
 
 238  178
         final int tokenType = ast.getType();
 239  
 
 240  
         //check if an expression is countable
 241  178
         if (tokenType == TokenTypes.EXPR) {
 242  63
             countable = isExpressionCountable(ast);
 243  
         }
 244  
         //check if an variable definition is countable
 245  115
         else if (tokenType == TokenTypes.VARIABLE_DEF) {
 246  36
             countable = isVariableDefCountable(ast);
 247  
         }
 248  178
         return countable;
 249  
     }
 250  
 
 251  
     /**
 252  
      * Checks if a variable definition is countable.
 253  
      *
 254  
      * @param ast the AST
 255  
      * @return true if the variable definition is countable, false otherwise
 256  
      */
 257  
     private static boolean isVariableDefCountable(DetailAST ast) {
 258  36
         boolean countable = false;
 259  
 
 260  
         //count variable definitions only if they are direct child to a slist or
 261  
         // object block
 262  36
         final int parentType = ast.getParent().getType();
 263  
 
 264  36
         if (parentType == TokenTypes.SLIST
 265  
             || parentType == TokenTypes.OBJBLOCK) {
 266  33
             final DetailAST prevSibling = ast.getPreviousSibling();
 267  
 
 268  
             //is countable if no previous sibling is found or
 269  
             //the sibling is no COMMA.
 270  
             //This is done because multiple assignment on one line are counted
 271  
             // as 1
 272  33
             countable = prevSibling == null
 273  18
                     || prevSibling.getType() != TokenTypes.COMMA;
 274  
         }
 275  
 
 276  36
         return countable;
 277  
     }
 278  
 
 279  
     /**
 280  
      * Checks if an expression is countable for the ncss metric.
 281  
      *
 282  
      * @param ast the AST
 283  
      * @return true if the expression is countable, false otherwise
 284  
      */
 285  
     private static boolean isExpressionCountable(DetailAST ast) {
 286  
         final boolean countable;
 287  
 
 288  
         //count expressions only if they are direct child to a slist (method
 289  
         // body, for loop...)
 290  
         //or direct child of label,if,else,do,while,for
 291  63
         final int parentType = ast.getParent().getType();
 292  63
         switch (parentType) {
 293  
             case TokenTypes.SLIST :
 294  
             case TokenTypes.LABELED_STAT :
 295  
             case TokenTypes.LITERAL_FOR :
 296  
             case TokenTypes.LITERAL_DO :
 297  
             case TokenTypes.LITERAL_WHILE :
 298  
             case TokenTypes.LITERAL_IF :
 299  
             case TokenTypes.LITERAL_ELSE :
 300  
                 //don't count if or loop conditions
 301  15
                 final DetailAST prevSibling = ast.getPreviousSibling();
 302  15
                 countable = prevSibling == null
 303  9
                     || prevSibling.getType() != TokenTypes.LPAREN;
 304  15
                 break;
 305  
             default :
 306  48
                 countable = false;
 307  
                 break;
 308  
         }
 309  63
         return countable;
 310  
     }
 311  
 
 312  
     /**
 313  
      * Class representing a counter.
 314  
      *
 315  
      * @author Lars K√∂dderitzsch
 316  
      */
 317  80
     private static class Counter {
 318  
         /** The counters internal integer. */
 319  
         private int count;
 320  
 
 321  
         /**
 322  
          * Increments the counter.
 323  
          */
 324  
         public void increment() {
 325  325
             count++;
 326  325
         }
 327  
 
 328  
         /**
 329  
          * Gets the counters value.
 330  
          *
 331  
          * @return the counter
 332  
          */
 333  
         public int getCount() {
 334  40
             return count;
 335  
         }
 336  
     }
 337  
 }