Coverage Report - com.puppycrawl.tools.checkstyle.checks.DescendantTokenCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
DescendantTokenCheck
98%
65/66
91%
33/36
2.5
 
 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;
 20  
 
 21  
 import antlr.collections.AST;
 22  
 import com.puppycrawl.tools.checkstyle.api.Check;
 23  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 24  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 25  
 import java.util.Arrays;
 26  
 import java.util.Set;
 27  
 
 28  
 /**
 29  
  * <p>
 30  
  * Checks for restricted tokens beneath other tokens.
 31  
  * </p>
 32  
  * <p>
 33  
  * Examples of how to configure the check:
 34  
  * </p>
 35  
  * <pre>
 36  
  * &lt;!-- String literal equality check --&gt;
 37  
  * &lt;module name="DescendantToken"&gt;
 38  
  *     &lt;property name="tokens" value="EQUAL,NOT_EQUAL"/&gt;
 39  
  *     &lt;property name="limitedTokens" value="STRING_LITERAL"/&gt;
 40  
  *     &lt;property name="maximumNumber" value="0"/&gt;
 41  
  *     &lt;property name="maximumDepth" value="1"/&gt;
 42  
  * &lt;/module&gt;
 43  
  *
 44  
  * &lt;!-- Switch with no default --&gt;
 45  
  * &lt;module name="DescendantToken"&gt;
 46  
  *     &lt;property name="tokens" value="LITERAL_SWITCH"/&gt;
 47  
  *     &lt;property name="maximumDepth" value="2"/&gt;
 48  
  *     &lt;property name="limitedTokens" value="LITERAL_DEFAULT"/&gt;
 49  
  *     &lt;property name="minimumNumber" value="1"/&gt;
 50  
  * &lt;/module&gt;
 51  
  *
 52  
  * &lt;!-- Assert statement may have side effects --&gt;
 53  
  * &lt;module name="DescendantToken"&gt;
 54  
  *     &lt;property name="tokens" value="LITERAL_ASSERT"/&gt;
 55  
  *     &lt;property name="limitedTokens" value="ASSIGN,DEC,INC,POST_DEC,
 56  
  *     POST_INC,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,DIV_ASSIGN,MOD_ASSIGN,
 57  
  *     BSR_ASSIGN,SR_ASSIGN,SL_ASSIGN,BAND_ASSIGN,BXOR_ASSIGN,BOR_ASSIGN,
 58  
  *     METHOD_CALL"/&gt;
 59  
  *     &lt;property name="maximumNumber" value="0"/&gt;
 60  
  * &lt;/module&gt;
 61  
  *
 62  
  * &lt;!-- Initialiser in for performs no setup - use while instead? --&gt;
 63  
  * &lt;module name="DescendantToken"&gt;
 64  
  *     &lt;property name="tokens" value="FOR_INIT"/&gt;
 65  
  *     &lt;property name="limitedTokens" value="EXPR"/&gt;
 66  
  *     &lt;property name="minimumNumber" value="1"/&gt;
 67  
  * &lt;/module&gt;
 68  
  *
 69  
  * &lt;!-- Condition in for performs no check --&gt;
 70  
  * &lt;module name="DescendantToken"&gt;
 71  
  *     &lt;property name="tokens" value="FOR_CONDITION"/&gt;
 72  
  *     &lt;property name="limitedTokens" value="EXPR"/&gt;
 73  
  *     &lt;property name="minimumNumber" value="1"/&gt;
 74  
  * &lt;/module&gt;
 75  
  *
 76  
  * &lt;!-- Switch within switch --&gt;
 77  
  * &lt;module name="DescendantToken"&gt;
 78  
  *     &lt;property name="tokens" value="LITERAL_SWITCH"/&gt;
 79  
  *     &lt;property name="limitedTokens" value="LITERAL_SWITCH"/&gt;
 80  
  *     &lt;property name="maximumNumber" value="0"/&gt;
 81  
  *     &lt;property name="minimumDepth" value="1"/&gt;
 82  
  * &lt;/module&gt;
 83  
  *
 84  
  * &lt;!-- Return from within a catch or finally block --&gt;
 85  
  * &lt;module name="DescendantToken"&gt;
 86  
  *     &lt;property name="tokens" value="LITERAL_FINALLY,LITERAL_CATCH"/&gt;
 87  
  *     &lt;property name="limitedTokens" value="LITERAL_RETURN"/&gt;
 88  
  *     &lt;property name="maximumNumber" value="0"/&gt;
 89  
  * &lt;/module&gt;
 90  
  *
 91  
  * &lt;!-- Try within catch or finally block --&gt;
 92  
  * &lt;module name="DescendantToken"&gt;
 93  
  *     &lt;property name="tokens" value="LITERAL_CATCH,LITERAL_FINALLY"/&gt;
 94  
  *     &lt;property name="limitedTokens" value="LITERAL_TRY"/&gt;
 95  
  *     &lt;property name="maximumNumber" value="0"/&gt;
 96  
  * &lt;/module&gt;
 97  
  *
 98  
  * &lt;!-- Too many cases within a switch --&gt;
 99  
  * &lt;module name="DescendantToken"&gt;
 100  
  *     &lt;property name="tokens" value="LITERAL_SWITCH"/&gt;
 101  
  *     &lt;property name="limitedTokens" value="LITERAL_CASE"/&gt;
 102  
  *     &lt;property name="maximumDepth" value="2"/&gt;
 103  
  *     &lt;property name="maximumNumber" value="10"/&gt;
 104  
  * &lt;/module&gt;
 105  
  *
 106  
  * &lt;!-- Too many local variables within a method --&gt;
 107  
  * &lt;module name="DescendantToken"&gt;
 108  
  *     &lt;property name="tokens" value="METHOD_DEF"/&gt;
 109  
  *     &lt;property name="limitedTokens" value="VARIABLE_DEF"/&gt;
 110  
  *     &lt;property name="maximumDepth" value="2"/&gt;
 111  
  *     &lt;property name="maximumNumber" value="10"/&gt;
 112  
  * &lt;/module&gt;
 113  
  *
 114  
  * &lt;!-- Too many returns from within a method --&gt;
 115  
  * &lt;module name="DescendantToken"&gt;
 116  
  *     &lt;property name="tokens" value="METHOD_DEF"/&gt;
 117  
  *     &lt;property name="limitedTokens" value="LITERAL_RETURN"/&gt;
 118  
  *     &lt;property name="maximumNumber" value="3"/&gt;
 119  
  * &lt;/module&gt;
 120  
  *
 121  
  * &lt;!-- Too many fields within an interface --&gt;
 122  
  * &lt;module name="DescendantToken"&gt;
 123  
  *     &lt;property name="tokens" value="INTERFACE_DEF"/&gt;
 124  
  *     &lt;property name="limitedTokens" value="VARIABLE_DEF"/&gt;
 125  
  *     &lt;property name="maximumDepth" value="2"/&gt;
 126  
  *     &lt;property name="maximumNumber" value="0"/&gt;
 127  
  * &lt;/module&gt;
 128  
  *
 129  
  * &lt;!-- Limit the number of exceptions a method can throw --&gt;
 130  
  * &lt;module name="DescendantToken"&gt;
 131  
  *     &lt;property name="tokens" value="LITERAL_THROWS"/&gt;
 132  
  *     &lt;property name="limitedTokens" value="IDENT"/&gt;
 133  
  *     &lt;property name="maximumNumber" value="1"/&gt;
 134  
  * &lt;/module&gt;
 135  
  *
 136  
  * &lt;!-- Limit the number of expressions in a method --&gt;
 137  
  * &lt;module name="DescendantToken"&gt;
 138  
  *     &lt;property name="tokens" value="METHOD_DEF"/&gt;
 139  
  *     &lt;property name="limitedTokens" value="EXPR"/&gt;
 140  
  *     &lt;property name="maximumNumber" value="200"/&gt;
 141  
  * &lt;/module&gt;
 142  
  *
 143  
  * &lt;!-- Disallow empty statements --&gt;
 144  
  * &lt;module name="DescendantToken"&gt;
 145  
  *     &lt;property name="tokens" value="EMPTY_STAT"/&gt;
 146  
  *     &lt;property name="limitedTokens" value="EMPTY_STAT"/&gt;
 147  
  *     &lt;property name="maximumNumber" value="0"/&gt;
 148  
  *     &lt;property name="maximumDepth" value="0"/&gt;
 149  
  *     &lt;property name="maximumMessage"
 150  
  *         value="Empty statement is not allowed."/&gt;
 151  
  * &lt;/module&gt;
 152  
  *
 153  
  * &lt;!-- Too many fields within a class --&gt;
 154  
  * &lt;module name="DescendantToken"&gt;
 155  
  *     &lt;property name="tokens" value="CLASS_DEF"/&gt;
 156  
  *     &lt;property name="limitedTokens" value="VARIABLE_DEF"/&gt;
 157  
  *     &lt;property name="maximumDepth" value="2"/&gt;
 158  
  *     &lt;property name="maximumNumber" value="10"/&gt;
 159  
  * &lt;/module&gt;
 160  
  * </pre>
 161  
  *
 162  
  * @author Tim Tyler &lt;tim@tt1.org&gt;
 163  
  * @author Rick Giles
 164  
  */
 165  18
 public class DescendantTokenCheck extends Check
 166  
 {
 167  
      /** minimum  depth */
 168  
     private int mMinimumDepth;
 169  
     /** maximum depth */
 170  18
     private int mMaximumDepth = Integer.MAX_VALUE;
 171  
     /** minimum number */
 172  
     private int mMinimumNumber;
 173  
     /** maximum number */
 174  18
     private int mMaximumNumber = Integer.MAX_VALUE;
 175  
     /** Whether to sum the number of tokens found. */
 176  
     private boolean mSumTokenCounts;
 177  
     /** limited tokens */
 178  18
     private int[] mLimitedTokens = new int[0];
 179  
     /** error message when minimum count not reached */
 180  
     private String mMinimumMessage;
 181  
     /** error message when maximum count exceeded */
 182  
     private String mMaximumMessage;
 183  
 
 184  
     /**
 185  
      * Counts of descendant tokens.
 186  
      * Indexed by (token ID - 1) for performance.
 187  
      */
 188  18
     private int[] mCounts = new int[0];
 189  
 
 190  
     @Override
 191  
     public int[] getDefaultTokens()
 192  
     {
 193  1
         return new int[0];
 194  
     }
 195  
 
 196  
     @Override
 197  
     public void visitToken(DetailAST aAST)
 198  
     {
 199  
         //reset counts
 200  61
         Arrays.fill(mCounts, 0);
 201  61
         countTokens(aAST, 0);
 202  
 
 203  
         // name of this token
 204  61
         final String name = TokenTypes.getTokenName(aAST.getType());
 205  
 
 206  61
         if (mSumTokenCounts) {
 207  14
             int total = 0;
 208  42
             for (int element : mLimitedTokens) {
 209  28
                 total += mCounts[element - 1];
 210  
             }
 211  14
             if (total < mMinimumNumber) {
 212  0
                 log(aAST.getLineNo(), aAST.getColumnNo(),
 213  
                         (null == mMinimumMessage) ? "descendant.token.sum.min"
 214  
                                 : mMinimumMessage,
 215  
                         String.valueOf(total),
 216  
                         String.valueOf(mMinimumNumber), name);
 217  
             }
 218  14
             if (total > mMaximumNumber) {
 219  8
                 log(aAST.getLineNo(), aAST.getColumnNo(),
 220  
                         (null == mMaximumMessage) ? "descendant.token.sum.max"
 221  
                                 : mMaximumMessage,
 222  
                         String.valueOf(total),
 223  
                         String.valueOf(mMaximumNumber),
 224  
                         name);
 225  
             }
 226  14
         }
 227  
         else {
 228  107
             for (int element : mLimitedTokens) {
 229  60
                 final int tokenCount = mCounts[element - 1];
 230  60
                 if (tokenCount < mMinimumNumber) {
 231  4
                     final String descendantName = TokenTypes
 232  
                             .getTokenName(element);
 233  4
                     log(aAST.getLineNo(), aAST.getColumnNo(),
 234  
                             (null == mMinimumMessage) ? "descendant.token.min"
 235  
                                     : mMinimumMessage,
 236  
                             String.valueOf(tokenCount),
 237  
                             String.valueOf(mMinimumNumber),
 238  
                             name,
 239  
                             descendantName);
 240  
                 }
 241  60
                 if (tokenCount > mMaximumNumber) {
 242  29
                     final String descendantName = TokenTypes
 243  
                             .getTokenName(element);
 244  29
                     log(aAST.getLineNo(), aAST.getColumnNo(),
 245  
                             (null == mMaximumMessage) ? "descendant.token.max"
 246  
                                     : mMaximumMessage,
 247  
                             String.valueOf(tokenCount),
 248  
                             String.valueOf(mMaximumNumber),
 249  
                             name,
 250  
                             descendantName);
 251  
                 }
 252  
             }
 253  
         }
 254  61
     }
 255  
 
 256  
     /**
 257  
      * Counts the number of occurrences of descendant tokens.
 258  
      * @param aAST the root token for descendants.
 259  
      * @param aDepth the maximum depth of the counted descendants.
 260  
      */
 261  
     private void countTokens(AST aAST, int aDepth)
 262  
     {
 263  414
         if (aDepth <= mMaximumDepth) {
 264  
             //update count
 265  323
             if (aDepth >= mMinimumDepth) {
 266  313
                 final int type = aAST.getType();
 267  313
                 if (type <= mCounts.length) {
 268  289
                     mCounts[type - 1]++;
 269  
                 }
 270  
             }
 271  323
             AST child = aAST.getFirstChild();
 272  323
             final int nextDepth = aDepth + 1;
 273  676
             while (child != null) {
 274  353
                 countTokens(child, nextDepth);
 275  353
                 child = child.getNextSibling();
 276  
             }
 277  
         }
 278  414
     }
 279  
 
 280  
     @Override
 281  
     public int[] getAcceptableTokens()
 282  
     {
 283  
         // Any tokens set by property 'tokens' are acceptable
 284  15
         final Set<String> tokenNames = getTokenNames();
 285  15
         final int[] result = new int[tokenNames.size()];
 286  15
         int i = 0;
 287  15
         for (String name : tokenNames) {
 288  21
             result[i++] = TokenTypes.getTokenId(name);
 289  
         }
 290  15
         return result;
 291  
     }
 292  
 
 293  
     /**
 294  
      * Sets the tokens which occurance as descendant is limited.
 295  
      * @param aLimitedTokens - list of tokens to ignore.
 296  
      */
 297  
     public void setLimitedTokens(String[] aLimitedTokens)
 298  
     {
 299  17
         mLimitedTokens = new int[aLimitedTokens.length];
 300  
 
 301  17
         int maxToken = 0;
 302  39
         for (int i = 0; i < aLimitedTokens.length; i++) {
 303  22
             mLimitedTokens[i] = TokenTypes.getTokenId(aLimitedTokens[i]);
 304  22
             if (mLimitedTokens[i] > maxToken) {
 305  20
                 maxToken = mLimitedTokens[i];
 306  
             }
 307  
         }
 308  17
         mCounts = new int[maxToken];
 309  17
     }
 310  
 
 311  
     /**
 312  
      * Sets the minimum depth for descendant counts.
 313  
      * @param aMinimumDepth the minimum depth for descendant counts.
 314  
      */
 315  
     public void setMinimumDepth(int aMinimumDepth)
 316  
     {
 317  1
         mMinimumDepth = aMinimumDepth;
 318  1
     }
 319  
 
 320  
     /**
 321  
      * Sets the maximum depth for descendant counts.
 322  
      * @param aMaximumDepth the maximum depth for descendant counts.
 323  
      */
 324  
     public void setMaximumDepth(int aMaximumDepth)
 325  
     {
 326  10
         mMaximumDepth = aMaximumDepth;
 327  10
     }
 328  
 
 329  
    /**
 330  
     * Sets a minimum count for descendants.
 331  
     * @param aMinimumNumber the minimum count for descendants.
 332  
     */
 333  
     public void setMinimumNumber(int aMinimumNumber)
 334  
     {
 335  4
         mMinimumNumber = aMinimumNumber;
 336  4
     }
 337  
 
 338  
     /**
 339  
       * Sets a maximum count for descendants.
 340  
       * @param aMaximumNumber the maximum count for descendants.
 341  
       */
 342  
     public void setMaximumNumber(int aMaximumNumber)
 343  
     {
 344  13
         mMaximumNumber = aMaximumNumber;
 345  13
     }
 346  
 
 347  
     /**
 348  
      * Sets the error message for minimum count not reached.
 349  
      * @param aMessage the error message for minimum count not reached.
 350  
      * Used as a <code>MessageFormat</code> pattern with arguments
 351  
      * <ul>
 352  
      * <li>{0} - token count</li>
 353  
      * <li>{1} - minimum number</li>
 354  
      * <li>{2} - name of token</li>
 355  
      * <li>{3} - name of limited token</li>
 356  
      * </ul>
 357  
      */
 358  
     public void setMinimumMessage(String aMessage)
 359  
     {
 360  3
         mMinimumMessage = aMessage;
 361  3
     }
 362  
 
 363  
     /**
 364  
      * Sets the error message for maximum count exceeded.
 365  
      * @param aMessage the error message for maximum count exceeded.
 366  
      * Used as a <code>MessageFormat</code> pattern with arguments
 367  
      * <ul>
 368  
      * <li>{0} - token count</li>
 369  
      * <li>{1} - maximum number</li>
 370  
      * <li>{2} - name of token</li>
 371  
      * <li>{3} - name of limited token</li>
 372  
      * </ul>
 373  
      */
 374  
 
 375  
     public void setMaximumMessage(String aMessage)
 376  
     {
 377  9
         mMaximumMessage = aMessage;
 378  9
     }
 379  
 
 380  
     /**
 381  
      * Sets whether to use the sum of the tokens found, rather than the
 382  
      * individual counts.
 383  
      * @param aSum whether to use the sum.
 384  
      */
 385  
     public void setSumTokenCounts(boolean aSum)
 386  
     {
 387  2
         mSumTokenCounts = aSum;
 388  2
     }
 389  
 }