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