001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 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//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks.sizes; 020 021import com.puppycrawl.tools.checkstyle.api.Check; 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.FastStack; 024import com.puppycrawl.tools.checkstyle.api.TokenTypes; 025 026/** 027 * Restricts the number of executable statements to a specified limit 028 * (default = 30). 029 * @author Simon Harris 030 */ 031public final class ExecutableStatementCountCheck 032 extends Check 033{ 034 /** default threshold */ 035 private static final int DEFAULT_MAX = 30; 036 037 /** threshold to report error for */ 038 private int max; 039 040 /** Stack of method contexts. */ 041 private final FastStack<Context> contextStack = FastStack.newInstance(); 042 043 /** Current method context. */ 044 private Context context; 045 046 /** Constructs a <code>ExecutableStatementCountCheck</code>. */ 047 public ExecutableStatementCountCheck() 048 { 049 setMax(DEFAULT_MAX); 050 } 051 052 @Override 053 public int[] getDefaultTokens() 054 { 055 return new int[] { 056 TokenTypes.CTOR_DEF, 057 TokenTypes.METHOD_DEF, 058 TokenTypes.INSTANCE_INIT, 059 TokenTypes.STATIC_INIT, 060 TokenTypes.SLIST, 061 }; 062 } 063 064 @Override 065 public int[] getRequiredTokens() 066 { 067 return new int[] {TokenTypes.SLIST}; 068 } 069 070 /** 071 * Gets the maximum threshold. 072 * @return the maximum threshold. 073 */ 074 public int getMax() 075 { 076 return max; 077 } 078 079 /** 080 * Sets the maximum threshold. 081 * @param max the maximum threshold. 082 */ 083 public void setMax(int max) 084 { 085 this.max = max; 086 } 087 088 @Override 089 public void beginTree(DetailAST rootAST) 090 { 091 context = null; 092 contextStack.clear(); 093 } 094 095 @Override 096 public void visitToken(DetailAST ast) 097 { 098 switch (ast.getType()) { 099 case TokenTypes.CTOR_DEF: 100 case TokenTypes.METHOD_DEF: 101 case TokenTypes.INSTANCE_INIT: 102 case TokenTypes.STATIC_INIT: 103 visitMemberDef(ast); 104 break; 105 case TokenTypes.SLIST: 106 visitSlist(ast); 107 break; 108 default: 109 throw new IllegalStateException(ast.toString()); 110 } 111 } 112 113 @Override 114 public void leaveToken(DetailAST ast) 115 { 116 switch (ast.getType()) { 117 case TokenTypes.CTOR_DEF: 118 case TokenTypes.METHOD_DEF: 119 case TokenTypes.INSTANCE_INIT: 120 case TokenTypes.STATIC_INIT: 121 leaveMemberDef(ast); 122 break; 123 case TokenTypes.SLIST: 124 // Do nothing 125 break; 126 default: 127 throw new IllegalStateException(ast.toString()); 128 } 129 } 130 131 /** 132 * Process the start of the member definition. 133 * @param ast the token representing the member definition. 134 */ 135 private void visitMemberDef(DetailAST ast) 136 { 137 contextStack.push(context); 138 context = new Context(ast); 139 } 140 141 /** 142 * Process the end of a member definition. 143 * 144 * @param ast the token representing the member definition. 145 */ 146 private void leaveMemberDef(DetailAST ast) 147 { 148 final int count = context.getCount(); 149 if (count > getMax()) { 150 log(ast.getLineNo(), ast.getColumnNo(), 151 "executableStatementCount", count, getMax()); 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 { 163 if (context != null) { 164 // find member AST for the statement list 165 final DetailAST contextAST = context.getAST(); 166 DetailAST parent = ast.getParent(); 167 while (parent != null) { 168 final int type = parent.getType(); 169 if ((type == TokenTypes.CTOR_DEF) 170 || (type == TokenTypes.METHOD_DEF) 171 || (type == TokenTypes.INSTANCE_INIT) 172 || (type == TokenTypes.STATIC_INIT)) 173 { 174 if (parent == contextAST) { 175 context.addCount(ast.getChildCount() / 2); 176 } 177 break; 178 } 179 parent = parent.getParent(); 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 public Context(DetailAST ast) 201 { 202 this.ast = ast; 203 count = 0; 204 } 205 206 /** 207 * Increase count. 208 * @param count the count increment. 209 */ 210 public void addCount(int count) 211 { 212 this.count += count; 213 } 214 215 /** 216 * Gets the member AST node. 217 * @return the member AST node. 218 */ 219 public DetailAST getAST() 220 { 221 return ast; 222 } 223 224 /** 225 * Gets the count. 226 * @return the count. 227 */ 228 public int getCount() 229 { 230 return count; 231 } 232 } 233}