001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 the original author or authors. 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//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.sizes; 021 022import java.util.ArrayDeque; 023import java.util.Deque; 024 025import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 026import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 027import com.puppycrawl.tools.checkstyle.api.DetailAST; 028import com.puppycrawl.tools.checkstyle.api.TokenTypes; 029 030/** 031 * <p> 032 * Restricts the number of executable statements to a specified limit. 033 * </p> 034 * <ul> 035 * <li> 036 * Property {@code max} - Specify the maximum threshold allowed. 037 * Type is {@code int}. 038 * Default value is {@code 30}. 039 * </li> 040 * <li> 041 * Property {@code tokens} - tokens to check 042 * Type is {@code int[]}. 043 * Default value is: 044 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF"> 045 * CTOR_DEF</a>, 046 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 047 * METHOD_DEF</a>, 048 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INSTANCE_INIT"> 049 * INSTANCE_INIT</a>, 050 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STATIC_INIT"> 051 * STATIC_INIT</a>. 052 * </li> 053 * </ul> 054 * <p> 055 * To configure the check: 056 * </p> 057 * <pre> 058 * <module name="ExecutableStatementCount"/> 059 * </pre> 060 * <p> 061 * To configure the check with a threshold of 20 for constructor and method definitions: 062 * </p> 063 * <pre> 064 * <module name="ExecutableStatementCount"> 065 * <property name="max" value="20"/> 066 * <property name="tokens" value="CTOR_DEF,METHOD_DEF"/> 067 * </module> 068 * </pre> 069 * <p> 070 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 071 * </p> 072 * <p> 073 * Violation Message Keys: 074 * </p> 075 * <ul> 076 * <li> 077 * {@code executableStatementCount} 078 * </li> 079 * </ul> 080 * 081 * @since 3.2 082 */ 083@FileStatefulCheck 084public final class ExecutableStatementCountCheck 085 extends AbstractCheck { 086 087 /** 088 * A key is pointing to the warning message text in "messages.properties" 089 * file. 090 */ 091 public static final String MSG_KEY = "executableStatementCount"; 092 093 /** Default threshold. */ 094 private static final int DEFAULT_MAX = 30; 095 096 /** Stack of method contexts. */ 097 private final Deque<Context> contextStack = new ArrayDeque<>(); 098 099 /** Specify the maximum threshold allowed. */ 100 private int max; 101 102 /** Current method context. */ 103 private Context context; 104 105 /** Constructs a {@code ExecutableStatementCountCheck}. */ 106 public ExecutableStatementCountCheck() { 107 max = DEFAULT_MAX; 108 } 109 110 @Override 111 public int[] getDefaultTokens() { 112 return new int[] { 113 TokenTypes.CTOR_DEF, 114 TokenTypes.METHOD_DEF, 115 TokenTypes.INSTANCE_INIT, 116 TokenTypes.STATIC_INIT, 117 TokenTypes.SLIST, 118 }; 119 } 120 121 @Override 122 public int[] getRequiredTokens() { 123 return new int[] {TokenTypes.SLIST}; 124 } 125 126 @Override 127 public int[] getAcceptableTokens() { 128 return new int[] { 129 TokenTypes.CTOR_DEF, 130 TokenTypes.METHOD_DEF, 131 TokenTypes.INSTANCE_INIT, 132 TokenTypes.STATIC_INIT, 133 TokenTypes.SLIST, 134 }; 135 } 136 137 /** 138 * Setter to specify the maximum threshold allowed. 139 * 140 * @param max the maximum threshold. 141 */ 142 public void setMax(int max) { 143 this.max = max; 144 } 145 146 @Override 147 public void beginTree(DetailAST rootAST) { 148 context = new Context(null); 149 contextStack.clear(); 150 } 151 152 @Override 153 public void visitToken(DetailAST ast) { 154 switch (ast.getType()) { 155 case TokenTypes.CTOR_DEF: 156 case TokenTypes.METHOD_DEF: 157 case TokenTypes.INSTANCE_INIT: 158 case TokenTypes.STATIC_INIT: 159 visitMemberDef(ast); 160 break; 161 case TokenTypes.SLIST: 162 visitSlist(ast); 163 break; 164 default: 165 throw new IllegalStateException(ast.toString()); 166 } 167 } 168 169 @Override 170 public void leaveToken(DetailAST ast) { 171 switch (ast.getType()) { 172 case TokenTypes.CTOR_DEF: 173 case TokenTypes.METHOD_DEF: 174 case TokenTypes.INSTANCE_INIT: 175 case TokenTypes.STATIC_INIT: 176 leaveMemberDef(ast); 177 break; 178 case TokenTypes.SLIST: 179 // Do nothing 180 break; 181 default: 182 throw new IllegalStateException(ast.toString()); 183 } 184 } 185 186 /** 187 * Process the start of the member definition. 188 * 189 * @param ast the token representing the member definition. 190 */ 191 private void visitMemberDef(DetailAST ast) { 192 contextStack.push(context); 193 context = new Context(ast); 194 } 195 196 /** 197 * Process the end of a member definition. 198 * 199 * @param ast the token representing the member definition. 200 */ 201 private void leaveMemberDef(DetailAST ast) { 202 final int count = context.getCount(); 203 if (count > max) { 204 log(ast, MSG_KEY, count, max); 205 } 206 context = contextStack.pop(); 207 } 208 209 /** 210 * Process the end of a statement list. 211 * 212 * @param ast the token representing the statement list. 213 */ 214 private void visitSlist(DetailAST ast) { 215 if (context.getAST() != null) { 216 // find member AST for the statement list 217 final DetailAST contextAST = context.getAST(); 218 DetailAST parent = ast.getParent(); 219 int type = parent.getType(); 220 while (type != TokenTypes.CTOR_DEF 221 && type != TokenTypes.METHOD_DEF 222 && type != TokenTypes.INSTANCE_INIT 223 && type != TokenTypes.STATIC_INIT) { 224 parent = parent.getParent(); 225 type = parent.getType(); 226 } 227 if (parent == contextAST) { 228 context.addCount(ast.getChildCount() / 2); 229 } 230 } 231 } 232 233 /** 234 * Class to encapsulate counting information about one member. 235 */ 236 private static class Context { 237 238 /** Member AST node. */ 239 private final DetailAST ast; 240 241 /** Counter for context elements. */ 242 private int count; 243 244 /** 245 * Creates new member context. 246 * 247 * @param ast member AST node. 248 */ 249 /* package */ Context(DetailAST ast) { 250 this.ast = ast; 251 count = 0; 252 } 253 254 /** 255 * Increase count. 256 * 257 * @param addition the count increment. 258 */ 259 public void addCount(int addition) { 260 count += addition; 261 } 262 263 /** 264 * Gets the member AST node. 265 * 266 * @return the member AST node. 267 */ 268 public DetailAST getAST() { 269 return ast; 270 } 271 272 /** 273 * Gets the count. 274 * 275 * @return the count. 276 */ 277 public int getCount() { 278 return count; 279 } 280 281 } 282 283}