001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2025 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; 029import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 030 031/** 032 * <div> 033 * Restricts the number of executable statements to a specified limit. 034 * </div> 035 * 036 * @since 3.2 037 */ 038@FileStatefulCheck 039public final class ExecutableStatementCountCheck 040 extends AbstractCheck { 041 042 /** 043 * A key is pointing to the warning message text in "messages.properties" 044 * file. 045 */ 046 public static final String MSG_KEY = "executableStatementCount"; 047 048 /** Default threshold. */ 049 private static final int DEFAULT_MAX = 30; 050 051 /** Stack of method contexts. */ 052 private final Deque<Context> contextStack = new ArrayDeque<>(); 053 054 /** Specify the maximum threshold allowed. */ 055 private int max; 056 057 /** Current method context. */ 058 private Context context; 059 060 /** Constructs a {@code ExecutableStatementCountCheck}. */ 061 public ExecutableStatementCountCheck() { 062 max = DEFAULT_MAX; 063 } 064 065 @Override 066 public int[] getDefaultTokens() { 067 return new int[] { 068 TokenTypes.CTOR_DEF, 069 TokenTypes.METHOD_DEF, 070 TokenTypes.INSTANCE_INIT, 071 TokenTypes.STATIC_INIT, 072 TokenTypes.SLIST, 073 TokenTypes.COMPACT_CTOR_DEF, 074 TokenTypes.LAMBDA, 075 }; 076 } 077 078 @Override 079 public int[] getRequiredTokens() { 080 return new int[] {TokenTypes.SLIST}; 081 } 082 083 @Override 084 public int[] getAcceptableTokens() { 085 return new int[] { 086 TokenTypes.CTOR_DEF, 087 TokenTypes.METHOD_DEF, 088 TokenTypes.INSTANCE_INIT, 089 TokenTypes.STATIC_INIT, 090 TokenTypes.SLIST, 091 TokenTypes.COMPACT_CTOR_DEF, 092 TokenTypes.LAMBDA, 093 }; 094 } 095 096 /** 097 * Setter to specify the maximum threshold allowed. 098 * 099 * @param max the maximum threshold. 100 * @since 3.2 101 */ 102 public void setMax(int max) { 103 this.max = max; 104 } 105 106 @Override 107 public void beginTree(DetailAST rootAST) { 108 context = new Context(null); 109 contextStack.clear(); 110 } 111 112 @Override 113 public void visitToken(DetailAST ast) { 114 if (isContainerNode(ast)) { 115 visitContainerNode(ast); 116 } 117 else if (TokenUtil.isOfType(ast, TokenTypes.SLIST)) { 118 visitSlist(ast); 119 } 120 else { 121 throw new IllegalStateException(ast.toString()); 122 } 123 } 124 125 @Override 126 public void leaveToken(DetailAST ast) { 127 if (isContainerNode(ast)) { 128 leaveContainerNode(ast); 129 } 130 else if (!TokenUtil.isOfType(ast, TokenTypes.SLIST)) { 131 throw new IllegalStateException(ast.toString()); 132 } 133 } 134 135 /** 136 * Process the start of the container node. 137 * 138 * @param ast the token representing the container node. 139 */ 140 private void visitContainerNode(DetailAST ast) { 141 contextStack.push(context); 142 context = new Context(ast); 143 } 144 145 /** 146 * Process the end of a container node. 147 * 148 * @param ast the token representing the container node. 149 */ 150 private void leaveContainerNode(DetailAST ast) { 151 final int count = context.getCount(); 152 if (count > max) { 153 log(ast, MSG_KEY, count, max); 154 } 155 context = contextStack.pop(); 156 } 157 158 /** 159 * Process the end of a statement list. 160 * 161 * @param ast the token representing the statement list. 162 */ 163 private void visitSlist(DetailAST ast) { 164 final DetailAST contextAST = context.getAST(); 165 DetailAST parent = ast; 166 while (parent != null && !isContainerNode(parent)) { 167 parent = parent.getParent(); 168 } 169 if (parent == contextAST) { 170 context.addCount(ast.getChildCount() / 2); 171 } 172 } 173 174 /** 175 * Check if the node is of type ctor (compact or canonical), 176 * instance/ static initializer, method definition or lambda. 177 * 178 * @param node AST node we are checking 179 * @return true if node is of the given types 180 */ 181 private static boolean isContainerNode(DetailAST node) { 182 return TokenUtil.isOfType(node, TokenTypes.METHOD_DEF, 183 TokenTypes.LAMBDA, TokenTypes.CTOR_DEF, TokenTypes.INSTANCE_INIT, 184 TokenTypes.STATIC_INIT, TokenTypes.COMPACT_CTOR_DEF); 185 } 186 187 /** 188 * Class to encapsulate counting information about one member. 189 */ 190 private static final class Context { 191 192 /** Member AST node. */ 193 private final DetailAST ast; 194 195 /** Counter for context elements. */ 196 private int count; 197 198 /** 199 * Creates new member context. 200 * 201 * @param ast member AST node. 202 */ 203 private Context(DetailAST ast) { 204 this.ast = ast; 205 } 206 207 /** 208 * Increase count. 209 * 210 * @param addition the count increment. 211 */ 212 public void addCount(int addition) { 213 count += addition; 214 } 215 216 /** 217 * Gets the member AST node. 218 * 219 * @return the member AST node. 220 */ 221 public DetailAST getAST() { 222 return ast; 223 } 224 225 /** 226 * Gets the count. 227 * 228 * @return the count. 229 */ 230 public int getCount() { 231 return count; 232 } 233 234 } 235 236}