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 mMax; 039 040 /** Stack of method contexts. */ 041 private final FastStack<Context> mContextStack = FastStack.newInstance(); 042 043 /** Current method context. */ 044 private Context mContext; 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 mMax; 077 } 078 079 /** 080 * Sets the maximum threshold. 081 * @param aMax the maximum threshold. 082 */ 083 public void setMax(int aMax) 084 { 085 mMax = aMax; 086 } 087 088 @Override 089 public void beginTree(DetailAST aRootAST) 090 { 091 mContext = null; 092 mContextStack.clear(); 093 } 094 095 @Override 096 public void visitToken(DetailAST aAST) 097 { 098 switch (aAST.getType()) { 099 case TokenTypes.CTOR_DEF: 100 case TokenTypes.METHOD_DEF: 101 case TokenTypes.INSTANCE_INIT: 102 case TokenTypes.STATIC_INIT: 103 visitMemberDef(aAST); 104 break; 105 case TokenTypes.SLIST: 106 visitSlist(aAST); 107 break; 108 default: 109 throw new IllegalStateException(aAST.toString()); 110 } 111 } 112 113 @Override 114 public void leaveToken(DetailAST aAST) 115 { 116 switch (aAST.getType()) { 117 case TokenTypes.CTOR_DEF: 118 case TokenTypes.METHOD_DEF: 119 case TokenTypes.INSTANCE_INIT: 120 case TokenTypes.STATIC_INIT: 121 leaveMemberDef(aAST); 122 break; 123 case TokenTypes.SLIST: 124 // Do nothing 125 break; 126 default: 127 throw new IllegalStateException(aAST.toString()); 128 } 129 } 130 131 /** 132 * Process the start of the member definition. 133 * @param aAST the token representing the member definition. 134 */ 135 private void visitMemberDef(DetailAST aAST) 136 { 137 mContextStack.push(mContext); 138 mContext = new Context(aAST); 139 } 140 141 /** 142 * Process the end of a member definition. 143 * 144 * @param aAST the token representing the member definition. 145 */ 146 private void leaveMemberDef(DetailAST aAST) 147 { 148 final int count = mContext.getCount(); 149 if (count > getMax()) { 150 log(aAST.getLineNo(), aAST.getColumnNo(), 151 "executableStatementCount", count, getMax()); 152 } 153 mContext = mContextStack.pop(); 154 } 155 156 /** 157 * Process the end of a statement list. 158 * 159 * @param aAST the token representing the statement list. 160 */ 161 private void visitSlist(DetailAST aAST) 162 { 163 if (mContext != null) { 164 // find member AST for the statement list 165 final DetailAST contextAST = mContext.getAST(); 166 DetailAST parent = aAST.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 mContext.addCount(aAST.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 mAST; 192 193 /** Counter for context elements. */ 194 private int mCount; 195 196 /** 197 * Creates new member context. 198 * @param aAST member AST node. 199 */ 200 public Context(DetailAST aAST) 201 { 202 mAST = aAST; 203 mCount = 0; 204 } 205 206 /** 207 * Increase count. 208 * @param aCount the count increment. 209 */ 210 public void addCount(int aCount) 211 { 212 mCount += aCount; 213 } 214 215 /** 216 * Gets the member AST node. 217 * @return the member AST node. 218 */ 219 public DetailAST getAST() 220 { 221 return mAST; 222 } 223 224 /** 225 * Gets the count. 226 * @return the count. 227 */ 228 public int getCount() 229 { 230 return mCount; 231 } 232 } 233}