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> counts = 046 new EnumMap<Scope, Integer>(Scope.class); 047 /** indicated is an interface, in which case all methods are public */ 048 private final boolean inInterface; 049 /** tracks the total. */ 050 private int total; 051 052 /** 053 * Creates an interface. 054 * @param inInterface indicated if counter for an interface. In which 055 * case, add all counts as public methods. 056 */ 057 MethodCounter(boolean inInterface) 058 { 059 this.inInterface = inInterface; 060 } 061 062 /** 063 * Increments to counter by one for the supplied scope. 064 * @param scope the scope counter to increment. 065 */ 066 void increment(Scope scope) 067 { 068 total++; 069 if (inInterface) { 070 counts.put(Scope.PUBLIC, 1 + value(Scope.PUBLIC)); 071 } 072 else { 073 counts.put(scope, 1 + value(scope)); 074 } 075 } 076 077 /** 078 * @return the value of a scope counter 079 * @param scope the scope counter to get the value of 080 */ 081 int value(Scope scope) 082 { 083 final Integer value = counts.get(scope); 084 return (null == value) ? 0 : value; 085 } 086 087 /** @return the total number of methods. */ 088 int getTotal() 089 { 090 return total; 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 maxPrivate = DEFAULT_MAX_METHODS; 098 /** Maximum package methods. */ 099 private int maxPackage = DEFAULT_MAX_METHODS; 100 /** Maximum protected methods. */ 101 private int maxProtected = DEFAULT_MAX_METHODS; 102 /** Maximum public methods. */ 103 private int maxPublic = DEFAULT_MAX_METHODS; 104 /** Maximum total number of methods. */ 105 private int maxTotal = DEFAULT_MAX_METHODS; 106 /** Maintains stack of counters, to support inner types. */ 107 private final FastStack<MethodCounter> counters = 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 ast) 124 { 125 if ((TokenTypes.CLASS_DEF == ast.getType()) 126 || (TokenTypes.INTERFACE_DEF == ast.getType()) 127 || (TokenTypes.ENUM_CONSTANT_DEF == ast.getType()) 128 || (TokenTypes.ENUM_DEF == ast.getType())) 129 { 130 counters.push(new MethodCounter( 131 TokenTypes.INTERFACE_DEF == ast.getType())); 132 } 133 else if (TokenTypes.METHOD_DEF == ast.getType()) { 134 raiseCounter(ast); 135 } 136 } 137 138 @Override 139 public void leaveToken(DetailAST ast) 140 { 141 if ((TokenTypes.CLASS_DEF == ast.getType()) 142 || (TokenTypes.INTERFACE_DEF == ast.getType()) 143 || (TokenTypes.ENUM_CONSTANT_DEF == ast.getType()) 144 || (TokenTypes.ENUM_DEF == ast.getType())) 145 { 146 final MethodCounter counter = counters.pop(); 147 checkCounters(counter, ast); 148 } 149 } 150 151 /** 152 * Determine the visibility modifier and raise the corresponding counter. 153 * @param method 154 * The method-subtree from the AbstractSyntaxTree. 155 */ 156 private void raiseCounter(DetailAST method) 157 { 158 final MethodCounter actualCounter = counters.peek(); 159 final DetailAST temp = method.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 counter the method counters to check 167 * @param ast to report errors against. 168 */ 169 private void checkCounters(MethodCounter counter, DetailAST ast) 170 { 171 checkMax(maxPrivate, counter.value(Scope.PRIVATE), 172 "too.many.privateMethods", ast); 173 checkMax(maxPackage, counter.value(Scope.PACKAGE), 174 "too.many.packageMethods", ast); 175 checkMax(maxProtected, counter.value(Scope.PROTECTED), 176 "too.many.protectedMethods", ast); 177 checkMax(maxPublic, counter.value(Scope.PUBLIC), 178 "too.many.publicMethods", ast); 179 checkMax(maxTotal, counter.getTotal(), "too.many.methods", ast); 180 } 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 { 191 if (max < value) { 192 log(ast.getLineNo(), msg, value, max); 193 } 194 } 195 196 /** 197 * Sets the maximum allowed <code>private</code> methods per type. 198 * @param value the maximum allowed. 199 */ 200 public void setMaxPrivate(int value) 201 { 202 maxPrivate = value; 203 } 204 205 /** 206 * Sets the maximum allowed <code>package</code> methods per type. 207 * @param value the maximum allowed. 208 */ 209 public void setMaxPackage(int value) 210 { 211 maxPackage = value; 212 } 213 214 /** 215 * Sets the maximum allowed <code>protected</code> methods per type. 216 * @param value the maximum allowed. 217 */ 218 public void setMaxProtected(int value) 219 { 220 maxProtected = value; 221 } 222 223 /** 224 * Sets the maximum allowed <code>public</code> methods per type. 225 * @param value the maximum allowed. 226 */ 227 public void setMaxPublic(int value) 228 { 229 maxPublic = value; 230 } 231 232 /** 233 * Sets the maximum total methods per type. 234 * @param value the maximum allowed. 235 */ 236 public void setMaxTotal(int value) 237 { 238 maxTotal = value; 239 } 240}