Coverage Report - com.puppycrawl.tools.checkstyle.checks.sizes.MethodCountCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
MethodCountCheck
100%
48/48
100%
12/12
1.368
MethodCountCheck$MethodCounter
100%
17/17
100%
6/6
1.368
 
 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.sizes;
 21  
 
 22  
 import java.util.ArrayDeque;
 23  
 import java.util.Deque;
 24  
 import java.util.EnumMap;
 25  
 import java.util.Map;
 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 the number of methods declared in each type declaration by access
 36  
  * modifier or total count.
 37  
  * @author Alexander Jesse
 38  
  * @author Oliver Burn
 39  
  */
 40  
 @FileStatefulCheck
 41  19
 public final class MethodCountCheck extends AbstractCheck {
 42  
 
 43  
     /**
 44  
      * A key is pointing to the warning message text in "messages.properties"
 45  
      * file.
 46  
      */
 47  
     public static final String MSG_PRIVATE_METHODS = "too.many.privateMethods";
 48  
 
 49  
     /**
 50  
      * A key is pointing to the warning message text in "messages.properties"
 51  
      * file.
 52  
      */
 53  
     public static final String MSG_PACKAGE_METHODS = "too.many.packageMethods";
 54  
 
 55  
     /**
 56  
      * A key is pointing to the warning message text in "messages.properties"
 57  
      * file.
 58  
      */
 59  
     public static final String MSG_PROTECTED_METHODS = "too.many.protectedMethods";
 60  
 
 61  
     /**
 62  
      * A key is pointing to the warning message text in "messages.properties"
 63  
      * file.
 64  
      */
 65  
     public static final String MSG_PUBLIC_METHODS = "too.many.publicMethods";
 66  
 
 67  
     /**
 68  
      * A key is pointing to the warning message text in "messages.properties"
 69  
      * file.
 70  
      */
 71  
     public static final String MSG_MANY_METHODS = "too.many.methods";
 72  
 
 73  
     /** Default maximum number of methods. */
 74  
     private static final int DEFAULT_MAX_METHODS = 100;
 75  
 
 76  
     /** Maintains stack of counters, to support inner types. */
 77  19
     private final Deque<MethodCounter> counters = new ArrayDeque<>();
 78  
 
 79  
     /** Maximum private methods. */
 80  19
     private int maxPrivate = DEFAULT_MAX_METHODS;
 81  
     /** Maximum package methods. */
 82  19
     private int maxPackage = DEFAULT_MAX_METHODS;
 83  
     /** Maximum protected methods. */
 84  19
     private int maxProtected = DEFAULT_MAX_METHODS;
 85  
     /** Maximum public methods. */
 86  19
     private int maxPublic = DEFAULT_MAX_METHODS;
 87  
     /** Maximum total number of methods. */
 88  19
     private int maxTotal = DEFAULT_MAX_METHODS;
 89  
 
 90  
     @Override
 91  
     public int[] getDefaultTokens() {
 92  27
         return getAcceptableTokens();
 93  
     }
 94  
 
 95  
     @Override
 96  
     public int[] getAcceptableTokens() {
 97  35
         return new int[] {
 98  
             TokenTypes.CLASS_DEF,
 99  
             TokenTypes.ENUM_CONSTANT_DEF,
 100  
             TokenTypes.ENUM_DEF,
 101  
             TokenTypes.INTERFACE_DEF,
 102  
             TokenTypes.ANNOTATION_DEF,
 103  
             TokenTypes.METHOD_DEF,
 104  
         };
 105  
     }
 106  
 
 107  
     @Override
 108  
     public int[] getRequiredTokens() {
 109  33
         return new int[] {TokenTypes.METHOD_DEF};
 110  
     }
 111  
 
 112  
     @Override
 113  
     public void visitToken(DetailAST ast) {
 114  95
         if (ast.getType() == TokenTypes.METHOD_DEF) {
 115  81
             if (isInLastestScopeDefinition(ast)) {
 116  75
                 raiseCounter(ast);
 117  
             }
 118  
         }
 119  
         else {
 120  14
             counters.push(new MethodCounter(ast));
 121  
         }
 122  95
     }
 123  
 
 124  
     @Override
 125  
     public void leaveToken(DetailAST ast) {
 126  95
         if (ast.getType() != TokenTypes.METHOD_DEF) {
 127  14
             final MethodCounter counter = counters.pop();
 128  
 
 129  14
             checkCounters(counter, ast);
 130  
         }
 131  95
     }
 132  
 
 133  
     /**
 134  
      * Checks if there is a scope definition to check and that the method is found inside that scope
 135  
      * (class, enum, etc.).
 136  
      * @param methodDef
 137  
      *        The method to analzye.
 138  
      * @return {@code true} if the method is part of the latest scope definition and should be
 139  
      *         counted.
 140  
      */
 141  
     private boolean isInLastestScopeDefinition(DetailAST methodDef) {
 142  81
         boolean result = false;
 143  
 
 144  81
         if (!counters.isEmpty()) {
 145  78
             final DetailAST latestDefinition = counters.peek().getScopeDefinition();
 146  
 
 147  78
             result = latestDefinition == methodDef.getParent().getParent();
 148  
         }
 149  
 
 150  81
         return result;
 151  
     }
 152  
 
 153  
     /**
 154  
      * Determine the visibility modifier and raise the corresponding counter.
 155  
      * @param method
 156  
      *            The method-subtree from the AbstractSyntaxTree.
 157  
      */
 158  
     private void raiseCounter(DetailAST method) {
 159  75
         final MethodCounter actualCounter = counters.peek();
 160  75
         final DetailAST temp = method.findFirstToken(TokenTypes.MODIFIERS);
 161  75
         final Scope scope = ScopeUtils.getScopeFromMods(temp);
 162  75
         actualCounter.increment(scope);
 163  75
     }
 164  
 
 165  
     /**
 166  
      * Check the counters and report violations.
 167  
      * @param counter the method counters to check
 168  
      * @param ast to report errors against.
 169  
      */
 170  
     private void checkCounters(MethodCounter counter, DetailAST ast) {
 171  14
         checkMax(maxPrivate, counter.value(Scope.PRIVATE),
 172  
                  MSG_PRIVATE_METHODS, ast);
 173  14
         checkMax(maxPackage, counter.value(Scope.PACKAGE),
 174  
                  MSG_PACKAGE_METHODS, ast);
 175  14
         checkMax(maxProtected, counter.value(Scope.PROTECTED),
 176  
                  MSG_PROTECTED_METHODS, ast);
 177  14
         checkMax(maxPublic, counter.value(Scope.PUBLIC),
 178  
                  MSG_PUBLIC_METHODS, ast);
 179  14
         checkMax(maxTotal, counter.getTotal(), MSG_MANY_METHODS, ast);
 180  14
     }
 181  
 
 182  
     /**
 183  
      * Utility for reporting if a maximum has been exceeded.
 184  
      * @param max the maximum allowed value
 185  
      * @param value the actual value
 186  
      * @param msg the message to log. Takes two arguments of value and maximum.
 187  
      * @param ast the AST to associate with the message.
 188  
      */
 189  
     private void checkMax(int max, int value, String msg, DetailAST ast) {
 190  70
         if (max < value) {
 191  14
             log(ast.getLineNo(), msg, value, max);
 192  
         }
 193  70
     }
 194  
 
 195  
     /**
 196  
      * Sets the maximum allowed {@code private} methods per type.
 197  
      * @param value the maximum allowed.
 198  
      */
 199  
     public void setMaxPrivate(int value) {
 200  3
         maxPrivate = value;
 201  3
     }
 202  
 
 203  
     /**
 204  
      * Sets the maximum allowed {@code package} methods per type.
 205  
      * @param value the maximum allowed.
 206  
      */
 207  
     public void setMaxPackage(int value) {
 208  1
         maxPackage = value;
 209  1
     }
 210  
 
 211  
     /**
 212  
      * Sets the maximum allowed {@code protected} methods per type.
 213  
      * @param value the maximum allowed.
 214  
      */
 215  
     public void setMaxProtected(int value) {
 216  1
         maxProtected = value;
 217  1
     }
 218  
 
 219  
     /**
 220  
      * Sets the maximum allowed {@code public} methods per type.
 221  
      * @param value the maximum allowed.
 222  
      */
 223  
     public void setMaxPublic(int value) {
 224  2
         maxPublic = value;
 225  2
     }
 226  
 
 227  
     /**
 228  
      * Sets the maximum total methods per type.
 229  
      * @param value the maximum allowed.
 230  
      */
 231  
     public void setMaxTotal(int value) {
 232  8
         maxTotal = value;
 233  8
     }
 234  
 
 235  
     /**
 236  
      * Marker class used to collect data about the number of methods per
 237  
      * class. Objects of this class are used on the Stack to count the
 238  
      * methods for each class and layer.
 239  
      */
 240  223
     private static class MethodCounter {
 241  
         /** Maintains the counts. */
 242  14
         private final Map<Scope, Integer> counts = new EnumMap<>(Scope.class);
 243  
         /** Indicated is an interface, in which case all methods are public. */
 244  
         private final boolean inInterface;
 245  
         /**
 246  
          * The surrounding scope definition (class, enum, etc.) which the method counts are connect
 247  
          * to.
 248  
          */
 249  
         private final DetailAST scopeDefinition;
 250  
         /** Tracks the total. */
 251  
         private int total;
 252  
 
 253  
         /**
 254  
          * Creates an interface.
 255  
          * @param scopeDefinition
 256  
          *        The surrounding scope definition (class, enum, etc.) which to count all methods
 257  
          *        for.
 258  
          */
 259  14
         MethodCounter(DetailAST scopeDefinition) {
 260  14
             this.scopeDefinition = scopeDefinition;
 261  14
             inInterface = scopeDefinition.getType() == TokenTypes.INTERFACE_DEF;
 262  14
         }
 263  
 
 264  
         /**
 265  
          * Increments to counter by one for the supplied scope.
 266  
          * @param scope the scope counter to increment.
 267  
          */
 268  
         private void increment(Scope scope) {
 269  75
             total++;
 270  75
             if (inInterface) {
 271  10
                 counts.put(Scope.PUBLIC, 1 + value(Scope.PUBLIC));
 272  
             }
 273  
             else {
 274  65
                 counts.put(scope, 1 + value(scope));
 275  
             }
 276  75
         }
 277  
 
 278  
         /**
 279  
          * Gets the value of a scope counter.
 280  
          * @param scope the scope counter to get the value of
 281  
          * @return the value of a scope counter
 282  
          */
 283  
         private int value(Scope scope) {
 284  131
             Integer value = counts.get(scope);
 285  131
             if (value == null) {
 286  56
                 value = 0;
 287  
             }
 288  131
             return value;
 289  
         }
 290  
 
 291  
         private DetailAST getScopeDefinition() {
 292  78
             return scopeDefinition;
 293  
         }
 294  
 
 295  
         /**
 296  
          * Fetches total number of methods.
 297  
          * @return the total number of methods.
 298  
          */
 299  
         private int getTotal() {
 300  14
             return total;
 301  
         }
 302  
     }
 303  
 }