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