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