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