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