001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks.sizes; 020 021import com.puppycrawl.tools.checkstyle.api.Check; 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.FastStack; 024import com.puppycrawl.tools.checkstyle.api.Scope; 025import com.puppycrawl.tools.checkstyle.api.ScopeUtils; 026import com.puppycrawl.tools.checkstyle.api.TokenTypes; 027import java.util.EnumMap; 028 029/** 030 * Counts the methods of the type-definition and checks whether this 031 * count is higher than the configured limit. 032 * @author Alexander Jesse 033 * @author Oliver Burn 034 */ 035public final class MethodCountCheck extends Check 036{ 037 /** 038 * Marker class used to collect data about the number of methods per 039 * class. Objects of this class are used on the Stack to count the 040 * methods for each class and layer. 041 */ 042 private static class MethodCounter 043 { 044 /** Maintains the counts. */ 045 private final EnumMap<Scope, Integer> mCounts = 046 new EnumMap<Scope, Integer>(Scope.class); 047 /** indicated is an interface, in which case all methods are public */ 048 private final boolean mInInterface; 049 /** tracks the total. */ 050 private int mTotal; 051 052 /** 053 * Creates an interface. 054 * @param aInInterface indicated if counter for an interface. In which 055 * case, add all counts as public methods. 056 */ 057 MethodCounter(boolean aInInterface) 058 { 059 mInInterface = aInInterface; 060 } 061 062 /** 063 * Increments to counter by one for the supplied scope. 064 * @param aScope the scope counter to increment. 065 */ 066 void increment(Scope aScope) 067 { 068 mTotal++; 069 if (mInInterface) { 070 mCounts.put(Scope.PUBLIC, 1 + value(Scope.PUBLIC)); 071 } 072 else { 073 mCounts.put(aScope, 1 + value(aScope)); 074 } 075 } 076 077 /** 078 * @return the value of a scope counter 079 * @param aScope the scope counter to get the value of 080 */ 081 int value(Scope aScope) 082 { 083 final Integer value = mCounts.get(aScope); 084 return (null == value) ? 0 : value; 085 } 086 087 /** @return the total number of methods. */ 088 int getTotal() 089 { 090 return mTotal; 091 } 092 }; 093 094 /** default maximum number of methods */ 095 private static final int DEFAULT_MAX_METHODS = 100; 096 /** Maximum private methods. */ 097 private int mMaxPrivate = DEFAULT_MAX_METHODS; 098 /** Maximum package methods. */ 099 private int mMaxPackage = DEFAULT_MAX_METHODS; 100 /** Maximum protected methods. */ 101 private int mMaxProtected = DEFAULT_MAX_METHODS; 102 /** Maximum public methods. */ 103 private int mMaxPublic = DEFAULT_MAX_METHODS; 104 /** Maximum total number of methods. */ 105 private int mMaxTotal = DEFAULT_MAX_METHODS; 106 /** Maintains stack of counters, to support inner types. */ 107 private final FastStack<MethodCounter> mCounters = 108 new FastStack<MethodCounter>(); 109 110 @Override 111 public int[] getDefaultTokens() 112 { 113 return new int[] { 114 TokenTypes.CLASS_DEF, 115 TokenTypes.ENUM_CONSTANT_DEF, 116 TokenTypes.ENUM_DEF, 117 TokenTypes.INTERFACE_DEF, 118 TokenTypes.METHOD_DEF, 119 }; 120 } 121 122 @Override 123 public void visitToken(DetailAST aAST) 124 { 125 if ((TokenTypes.CLASS_DEF == aAST.getType()) 126 || (TokenTypes.INTERFACE_DEF == aAST.getType()) 127 || (TokenTypes.ENUM_CONSTANT_DEF == aAST.getType()) 128 || (TokenTypes.ENUM_DEF == aAST.getType())) 129 { 130 mCounters.push(new MethodCounter( 131 TokenTypes.INTERFACE_DEF == aAST.getType())); 132 } 133 else if (TokenTypes.METHOD_DEF == aAST.getType()) { 134 raiseCounter(aAST); 135 } 136 } 137 138 @Override 139 public void leaveToken(DetailAST aAST) 140 { 141 if ((TokenTypes.CLASS_DEF == aAST.getType()) 142 || (TokenTypes.INTERFACE_DEF == aAST.getType()) 143 || (TokenTypes.ENUM_CONSTANT_DEF == aAST.getType()) 144 || (TokenTypes.ENUM_DEF == aAST.getType())) 145 { 146 final MethodCounter counter = mCounters.pop(); 147 checkCounters(counter, aAST); 148 } 149 } 150 151 /** 152 * Determine the visibility modifier and raise the corresponding counter. 153 * @param aMethod 154 * The method-subtree from the AbstractSyntaxTree. 155 */ 156 private void raiseCounter(DetailAST aMethod) 157 { 158 final MethodCounter actualCounter = mCounters.peek(); 159 final DetailAST temp = aMethod.findFirstToken(TokenTypes.MODIFIERS); 160 final Scope scope = ScopeUtils.getScopeFromMods(temp); 161 actualCounter.increment(scope); 162 } 163 164 /** 165 * Check the counters and report violations. 166 * @param aCounter the method counters to check 167 * @param aAst to report errors against. 168 */ 169 private void checkCounters(MethodCounter aCounter, DetailAST aAst) 170 { 171 checkMax(mMaxPrivate, aCounter.value(Scope.PRIVATE), 172 "too.many.privateMethods", aAst); 173 checkMax(mMaxPackage, aCounter.value(Scope.PACKAGE), 174 "too.many.packageMethods", aAst); 175 checkMax(mMaxProtected, aCounter.value(Scope.PROTECTED), 176 "too.many.protectedMethods", aAst); 177 checkMax(mMaxPublic, aCounter.value(Scope.PUBLIC), 178 "too.many.publicMethods", aAst); 179 checkMax(mMaxTotal, aCounter.getTotal(), "too.many.methods", aAst); 180 } 181 182 /** 183 * Utility for reporting if a maximum has been exceeded. 184 * @param aMax the maximum allowed value 185 * @param aValue the actual value 186 * @param aMsg the message to log. Takes two arguments of value and maximum. 187 * @param aAst the AST to associate with the message. 188 */ 189 private void checkMax(int aMax, int aValue, String aMsg, DetailAST aAst) 190 { 191 if (aMax < aValue) { 192 log(aAst.getLineNo(), aMsg, aValue, aMax); 193 } 194 } 195 196 /** 197 * Sets the maximum allowed <code>private</code> methods per type. 198 * @param aValue the maximum allowed. 199 */ 200 public void setMaxPrivate(int aValue) 201 { 202 mMaxPrivate = aValue; 203 } 204 205 /** 206 * Sets the maximum allowed <code>package</code> methods per type. 207 * @param aValue the maximum allowed. 208 */ 209 public void setMaxPackage(int aValue) 210 { 211 mMaxPackage = aValue; 212 } 213 214 /** 215 * Sets the maximum allowed <code>protected</code> methods per type. 216 * @param aValue the maximum allowed. 217 */ 218 public void setMaxProtected(int aValue) 219 { 220 mMaxProtected = aValue; 221 } 222 223 /** 224 * Sets the maximum allowed <code>public</code> methods per type. 225 * @param aValue the maximum allowed. 226 */ 227 public void setMaxPublic(int aValue) 228 { 229 mMaxPublic = aValue; 230 } 231 232 /** 233 * Sets the maximum total methods per type. 234 * @param aValue the maximum allowed. 235 */ 236 public void setMaxTotal(int aValue) 237 { 238 mMaxTotal = aValue; 239 } 240}