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; 020 021import antlr.collections.AST; 022import com.puppycrawl.tools.checkstyle.api.Check; 023import com.puppycrawl.tools.checkstyle.api.DetailAST; 024import com.puppycrawl.tools.checkstyle.api.TokenTypes; 025import java.util.Arrays; 026import java.util.Set; 027 028/** 029 * <p> 030 * Checks for restricted tokens beneath other tokens. 031 * </p> 032 * <p> 033 * Examples of how to configure the check: 034 * </p> 035 * <pre> 036 * <!-- String literal equality check --> 037 * <module name="DescendantToken"> 038 * <property name="tokens" value="EQUAL,NOT_EQUAL"/> 039 * <property name="limitedTokens" value="STRING_LITERAL"/> 040 * <property name="maximumNumber" value="0"/> 041 * <property name="maximumDepth" value="1"/> 042 * </module> 043 * 044 * <!-- Switch with no default --> 045 * <module name="DescendantToken"> 046 * <property name="tokens" value="LITERAL_SWITCH"/> 047 * <property name="maximumDepth" value="2"/> 048 * <property name="limitedTokens" value="LITERAL_DEFAULT"/> 049 * <property name="minimumNumber" value="1"/> 050 * </module> 051 * 052 * <!-- Assert statement may have side effects --> 053 * <module name="DescendantToken"> 054 * <property name="tokens" value="LITERAL_ASSERT"/> 055 * <property name="limitedTokens" value="ASSIGN,DEC,INC,POST_DEC, 056 * POST_INC,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,DIV_ASSIGN,MOD_ASSIGN, 057 * BSR_ASSIGN,SR_ASSIGN,SL_ASSIGN,BAND_ASSIGN,BXOR_ASSIGN,BOR_ASSIGN, 058 * METHOD_CALL"/> 059 * <property name="maximumNumber" value="0"/> 060 * </module> 061 * 062 * <!-- Initialiser in for performs no setup - use while instead? --> 063 * <module name="DescendantToken"> 064 * <property name="tokens" value="FOR_INIT"/> 065 * <property name="limitedTokens" value="EXPR"/> 066 * <property name="minimumNumber" value="1"/> 067 * </module> 068 * 069 * <!-- Condition in for performs no check --> 070 * <module name="DescendantToken"> 071 * <property name="tokens" value="FOR_CONDITION"/> 072 * <property name="limitedTokens" value="EXPR"/> 073 * <property name="minimumNumber" value="1"/> 074 * </module> 075 * 076 * <!-- Switch within switch --> 077 * <module name="DescendantToken"> 078 * <property name="tokens" value="LITERAL_SWITCH"/> 079 * <property name="limitedTokens" value="LITERAL_SWITCH"/> 080 * <property name="maximumNumber" value="0"/> 081 * <property name="minimumDepth" value="1"/> 082 * </module> 083 * 084 * <!-- Return from within a catch or finally block --> 085 * <module name="DescendantToken"> 086 * <property name="tokens" value="LITERAL_FINALLY,LITERAL_CATCH"/> 087 * <property name="limitedTokens" value="LITERAL_RETURN"/> 088 * <property name="maximumNumber" value="0"/> 089 * </module> 090 * 091 * <!-- Try within catch or finally block --> 092 * <module name="DescendantToken"> 093 * <property name="tokens" value="LITERAL_CATCH,LITERAL_FINALLY"/> 094 * <property name="limitedTokens" value="LITERAL_TRY"/> 095 * <property name="maximumNumber" value="0"/> 096 * </module> 097 * 098 * <!-- Too many cases within a switch --> 099 * <module name="DescendantToken"> 100 * <property name="tokens" value="LITERAL_SWITCH"/> 101 * <property name="limitedTokens" value="LITERAL_CASE"/> 102 * <property name="maximumDepth" value="2"/> 103 * <property name="maximumNumber" value="10"/> 104 * </module> 105 * 106 * <!-- Too many local variables within a method --> 107 * <module name="DescendantToken"> 108 * <property name="tokens" value="METHOD_DEF"/> 109 * <property name="limitedTokens" value="VARIABLE_DEF"/> 110 * <property name="maximumDepth" value="2"/> 111 * <property name="maximumNumber" value="10"/> 112 * </module> 113 * 114 * <!-- Too many returns from within a method --> 115 * <module name="DescendantToken"> 116 * <property name="tokens" value="METHOD_DEF"/> 117 * <property name="limitedTokens" value="LITERAL_RETURN"/> 118 * <property name="maximumNumber" value="3"/> 119 * </module> 120 * 121 * <!-- Too many fields within an interface --> 122 * <module name="DescendantToken"> 123 * <property name="tokens" value="INTERFACE_DEF"/> 124 * <property name="limitedTokens" value="VARIABLE_DEF"/> 125 * <property name="maximumDepth" value="2"/> 126 * <property name="maximumNumber" value="0"/> 127 * </module> 128 * 129 * <!-- Limit the number of exceptions a method can throw --> 130 * <module name="DescendantToken"> 131 * <property name="tokens" value="LITERAL_THROWS"/> 132 * <property name="limitedTokens" value="IDENT"/> 133 * <property name="maximumNumber" value="1"/> 134 * </module> 135 * 136 * <!-- Limit the number of expressions in a method --> 137 * <module name="DescendantToken"> 138 * <property name="tokens" value="METHOD_DEF"/> 139 * <property name="limitedTokens" value="EXPR"/> 140 * <property name="maximumNumber" value="200"/> 141 * </module> 142 * 143 * <!-- Disallow empty statements --> 144 * <module name="DescendantToken"> 145 * <property name="tokens" value="EMPTY_STAT"/> 146 * <property name="limitedTokens" value="EMPTY_STAT"/> 147 * <property name="maximumNumber" value="0"/> 148 * <property name="maximumDepth" value="0"/> 149 * <property name="maximumMessage" 150 * value="Empty statement is not allowed."/> 151 * </module> 152 * 153 * <!-- Too many fields within a class --> 154 * <module name="DescendantToken"> 155 * <property name="tokens" value="CLASS_DEF"/> 156 * <property name="limitedTokens" value="VARIABLE_DEF"/> 157 * <property name="maximumDepth" value="2"/> 158 * <property name="maximumNumber" value="10"/> 159 * </module> 160 * </pre> 161 * 162 * @author Tim Tyler <tim@tt1.org> 163 * @author Rick Giles 164 */ 165public class DescendantTokenCheck extends Check 166{ 167 /** minimum depth */ 168 private int minimumDepth; 169 /** maximum depth */ 170 private int maximumDepth = Integer.MAX_VALUE; 171 /** minimum number */ 172 private int minimumNumber; 173 /** maximum number */ 174 private int maximumNumber = Integer.MAX_VALUE; 175 /** Whether to sum the number of tokens found. */ 176 private boolean sumTokenCounts; 177 /** limited tokens */ 178 private int[] limitedTokens = new int[0]; 179 /** error message when minimum count not reached */ 180 private String minimumMessage; 181 /** error message when maximum count exceeded */ 182 private String maximumMessage; 183 184 /** 185 * Counts of descendant tokens. 186 * Indexed by (token ID - 1) for performance. 187 */ 188 private int[] counts = new int[0]; 189 190 @Override 191 public int[] getDefaultTokens() 192 { 193 return new int[0]; 194 } 195 196 @Override 197 public void visitToken(DetailAST ast) 198 { 199 //reset counts 200 Arrays.fill(counts, 0); 201 countTokens(ast, 0); 202 203 // name of this token 204 final String name = TokenTypes.getTokenName(ast.getType()); 205 206 if (sumTokenCounts) { 207 int total = 0; 208 for (int element : limitedTokens) { 209 total += counts[element - 1]; 210 } 211 if (total < minimumNumber) { 212 log(ast.getLineNo(), ast.getColumnNo(), 213 (null == minimumMessage) ? "descendant.token.sum.min" 214 : minimumMessage, 215 String.valueOf(total), 216 String.valueOf(minimumNumber), name); 217 } 218 if (total > maximumNumber) { 219 log(ast.getLineNo(), ast.getColumnNo(), 220 (null == maximumMessage) ? "descendant.token.sum.max" 221 : maximumMessage, 222 String.valueOf(total), 223 String.valueOf(maximumNumber), 224 name); 225 } 226 } 227 else { 228 for (int element : limitedTokens) { 229 final int tokenCount = counts[element - 1]; 230 if (tokenCount < minimumNumber) { 231 final String descendantName = TokenTypes 232 .getTokenName(element); 233 log(ast.getLineNo(), ast.getColumnNo(), 234 (null == minimumMessage) ? "descendant.token.min" 235 : minimumMessage, 236 String.valueOf(tokenCount), 237 String.valueOf(minimumNumber), 238 name, 239 descendantName); 240 } 241 if (tokenCount > maximumNumber) { 242 final String descendantName = TokenTypes 243 .getTokenName(element); 244 log(ast.getLineNo(), ast.getColumnNo(), 245 (null == maximumMessage) ? "descendant.token.max" 246 : maximumMessage, 247 String.valueOf(tokenCount), 248 String.valueOf(maximumNumber), 249 name, 250 descendantName); 251 } 252 } 253 } 254 } 255 256 /** 257 * Counts the number of occurrences of descendant tokens. 258 * @param ast the root token for descendants. 259 * @param depth the maximum depth of the counted descendants. 260 */ 261 private void countTokens(AST ast, int depth) 262 { 263 if (depth <= maximumDepth) { 264 //update count 265 if (depth >= minimumDepth) { 266 final int type = ast.getType(); 267 if (type <= counts.length) { 268 counts[type - 1]++; 269 } 270 } 271 AST child = ast.getFirstChild(); 272 final int nextDepth = depth + 1; 273 while (child != null) { 274 countTokens(child, nextDepth); 275 child = child.getNextSibling(); 276 } 277 } 278 } 279 280 @Override 281 public int[] getAcceptableTokens() 282 { 283 // Any tokens set by property 'tokens' are acceptable 284 final Set<String> tokenNames = getTokenNames(); 285 final int[] result = new int[tokenNames.size()]; 286 int i = 0; 287 for (String name : tokenNames) { 288 result[i++] = TokenTypes.getTokenId(name); 289 } 290 return result; 291 } 292 293 /** 294 * Sets the tokens which occurance as descendant is limited. 295 * @param limitedTokensParam - list of tokens to ignore. 296 */ 297 public void setLimitedTokens(String[] limitedTokensParam) 298 { 299 limitedTokens = new int[limitedTokensParam.length]; 300 301 int maxToken = 0; 302 for (int i = 0; i < limitedTokensParam.length; i++) { 303 limitedTokens[i] = TokenTypes.getTokenId(limitedTokensParam[i]); 304 if (limitedTokens[i] > maxToken) { 305 maxToken = limitedTokens[i]; 306 } 307 } 308 counts = new int[maxToken]; 309 } 310 311 /** 312 * Sets the minimum depth for descendant counts. 313 * @param minimumDepth the minimum depth for descendant counts. 314 */ 315 public void setMinimumDepth(int minimumDepth) 316 { 317 this.minimumDepth = minimumDepth; 318 } 319 320 /** 321 * Sets the maximum depth for descendant counts. 322 * @param maximumDepth the maximum depth for descendant counts. 323 */ 324 public void setMaximumDepth(int maximumDepth) 325 { 326 this.maximumDepth = maximumDepth; 327 } 328 329 /** 330 * Sets a minimum count for descendants. 331 * @param minimumNumber the minimum count for descendants. 332 */ 333 public void setMinimumNumber(int minimumNumber) 334 { 335 this.minimumNumber = minimumNumber; 336 } 337 338 /** 339 * Sets a maximum count for descendants. 340 * @param maximumNumber the maximum count for descendants. 341 */ 342 public void setMaximumNumber(int maximumNumber) 343 { 344 this.maximumNumber = maximumNumber; 345 } 346 347 /** 348 * Sets the error message for minimum count not reached. 349 * @param message the error message for minimum count not reached. 350 * Used as a <code>MessageFormat</code> pattern with arguments 351 * <ul> 352 * <li>{0} - token count</li> 353 * <li>{1} - minimum number</li> 354 * <li>{2} - name of token</li> 355 * <li>{3} - name of limited token</li> 356 * </ul> 357 */ 358 public void setMinimumMessage(String message) 359 { 360 minimumMessage = message; 361 } 362 363 /** 364 * Sets the error message for maximum count exceeded. 365 * @param message the error message for maximum count exceeded. 366 * Used as a <code>MessageFormat</code> pattern with arguments 367 * <ul> 368 * <li>{0} - token count</li> 369 * <li>{1} - maximum number</li> 370 * <li>{2} - name of token</li> 371 * <li>{3} - name of limited token</li> 372 * </ul> 373 */ 374 375 public void setMaximumMessage(String message) 376 { 377 maximumMessage = message; 378 } 379 380 /** 381 * Sets whether to use the sum of the tokens found, rather than the 382 * individual counts. 383 * @param sum whether to use the sum. 384 */ 385 public void setSumTokenCounts(boolean sum) 386 { 387 sumTokenCounts = sum; 388 } 389}