All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.puppycrawl.tools.checkstyle.checks.DescendantTokenCheck Maven / Gradle / Ivy

Go to download

Checkstyle is a development tool to help programmers write Java code that adheres to a coding standard

There is a newer version: 10.18.1
Show newest version
////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2016 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
////////////////////////////////////////////////////////////////////////////////

package com.puppycrawl.tools.checkstyle.checks;

import java.util.Arrays;
import java.util.Set;

import antlr.collections.AST;

import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
import com.puppycrawl.tools.checkstyle.utils.TokenUtils;

/**
 * 

* Checks for restricted tokens beneath other tokens. *

*

* Examples of how to configure the check: *

*
 * <!-- String literal equality check -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="EQUAL,NOT_EQUAL"/>
 *     <property name="limitedTokens" value="STRING_LITERAL"/>
 *     <property name="maximumNumber" value="0"/>
 *     <property name="maximumDepth" value="1"/>
 * </module>
 *
 * <!-- Switch with no default -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="LITERAL_SWITCH"/>
 *     <property name="maximumDepth" value="2"/>
 *     <property name="limitedTokens" value="LITERAL_DEFAULT"/>
 *     <property name="minimumNumber" value="1"/>
 * </module>
 *
 * <!-- Assert statement may have side effects -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="LITERAL_ASSERT"/>
 *     <property name="limitedTokens" value="ASSIGN,DEC,INC,POST_DEC,
 *     POST_INC,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,DIV_ASSIGN,MOD_ASSIGN,
 *     BSR_ASSIGN,SR_ASSIGN,SL_ASSIGN,BAND_ASSIGN,BXOR_ASSIGN,BOR_ASSIGN,
 *     METHOD_CALL"/>
 *     <property name="maximumNumber" value="0"/>
 * </module>
 *
 * <!-- Initializer in for performs no setup - use while instead? -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="FOR_INIT"/>
 *     <property name="limitedTokens" value="EXPR"/>
 *     <property name="minimumNumber" value="1"/>
 * </module>
 *
 * <!-- Condition in for performs no check -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="FOR_CONDITION"/>
 *     <property name="limitedTokens" value="EXPR"/>
 *     <property name="minimumNumber" value="1"/>
 * </module>
 *
 * <!-- Switch within switch -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="LITERAL_SWITCH"/>
 *     <property name="limitedTokens" value="LITERAL_SWITCH"/>
 *     <property name="maximumNumber" value="0"/>
 *     <property name="minimumDepth" value="1"/>
 * </module>
 *
 * <!-- Return from within a catch or finally block -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="LITERAL_FINALLY,LITERAL_CATCH"/>
 *     <property name="limitedTokens" value="LITERAL_RETURN"/>
 *     <property name="maximumNumber" value="0"/>
 * </module>
 *
 * <!-- Try within catch or finally block -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="LITERAL_CATCH,LITERAL_FINALLY"/>
 *     <property name="limitedTokens" value="LITERAL_TRY"/>
 *     <property name="maximumNumber" value="0"/>
 * </module>
 *
 * <!-- Too many cases within a switch -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="LITERAL_SWITCH"/>
 *     <property name="limitedTokens" value="LITERAL_CASE"/>
 *     <property name="maximumDepth" value="2"/>
 *     <property name="maximumNumber" value="10"/>
 * </module>
 *
 * <!-- Too many local variables within a method -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="METHOD_DEF"/>
 *     <property name="limitedTokens" value="VARIABLE_DEF"/>
 *     <property name="maximumDepth" value="2"/>
 *     <property name="maximumNumber" value="10"/>
 * </module>
 *
 * <!-- Too many returns from within a method -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="METHOD_DEF"/>
 *     <property name="limitedTokens" value="LITERAL_RETURN"/>
 *     <property name="maximumNumber" value="3"/>
 * </module>
 *
 * <!-- Too many fields within an interface -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="INTERFACE_DEF"/>
 *     <property name="limitedTokens" value="VARIABLE_DEF"/>
 *     <property name="maximumDepth" value="2"/>
 *     <property name="maximumNumber" value="0"/>
 * </module>
 *
 * <!-- Limit the number of exceptions a method can throw -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="LITERAL_THROWS"/>
 *     <property name="limitedTokens" value="IDENT"/>
 *     <property name="maximumNumber" value="1"/>
 * </module>
 *
 * <!-- Limit the number of expressions in a method -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="METHOD_DEF"/>
 *     <property name="limitedTokens" value="EXPR"/>
 *     <property name="maximumNumber" value="200"/>
 * </module>
 *
 * <!-- Disallow empty statements -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="EMPTY_STAT"/>
 *     <property name="limitedTokens" value="EMPTY_STAT"/>
 *     <property name="maximumNumber" value="0"/>
 *     <property name="maximumDepth" value="0"/>
 *     <property name="maximumMessage"
 *         value="Empty statement is not allowed."/>
 * </module>
 *
 * <!-- Too many fields within a class -->
 * <module name="DescendantToken">
 *     <property name="tokens" value="CLASS_DEF"/>
 *     <property name="limitedTokens" value="VARIABLE_DEF"/>
 *     <property name="maximumDepth" value="2"/>
 *     <property name="maximumNumber" value="10"/>
 * </module>
 * 
* * @author Tim Tyler <[email protected]> * @author Rick Giles */ public class DescendantTokenCheck extends AbstractCheck { /** * A key is pointing to the warning message text in "messages.properties" * file. */ public static final String MSG_KEY_MIN = "descendant.token.min"; /** * A key is pointing to the warning message text in "messages.properties" * file. */ public static final String MSG_KEY_MAX = "descendant.token.max"; /** * A key is pointing to the warning message text in "messages.properties" * file. */ public static final String MSG_KEY_SUM_MIN = "descendant.token.sum.min"; /** * A key is pointing to the warning message text in "messages.properties" * file. */ public static final String MSG_KEY_SUM_MAX = "descendant.token.sum.max"; /** Minimum depth. */ private int minimumDepth; /** Maximum depth. */ private int maximumDepth = Integer.MAX_VALUE; /** Minimum number. */ private int minimumNumber; /** Maximum number. */ private int maximumNumber = Integer.MAX_VALUE; /** Whether to sum the number of tokens found. */ private boolean sumTokenCounts; /** Limited tokens. */ private int[] limitedTokens = CommonUtils.EMPTY_INT_ARRAY; /** Error message when minimum count not reached. */ private String minimumMessage; /** Error message when maximum count exceeded. */ private String maximumMessage; /** * Counts of descendant tokens. * Indexed by (token ID - 1) for performance. */ private int[] counts = CommonUtils.EMPTY_INT_ARRAY; @Override public int[] getDefaultTokens() { return CommonUtils.EMPTY_INT_ARRAY; } @Override public int[] getRequiredTokens() { return CommonUtils.EMPTY_INT_ARRAY; } @Override public void visitToken(DetailAST ast) { //reset counts Arrays.fill(counts, 0); countTokens(ast, 0); if (sumTokenCounts) { logAsTotal(ast); } else { logAsSeparated(ast); } } /** * Log violations for each Token. * @param ast token */ private void logAsSeparated(DetailAST ast) { // name of this token final String name = TokenUtils.getTokenName(ast.getType()); for (int element : limitedTokens) { final int tokenCount = counts[element - 1]; if (tokenCount < minimumNumber) { final String descendantName = TokenUtils.getTokenName(element); if (minimumMessage == null) { minimumMessage = MSG_KEY_MIN; } log(ast.getLineNo(), ast.getColumnNo(), minimumMessage, String.valueOf(tokenCount), String.valueOf(minimumNumber), name, descendantName); } if (tokenCount > maximumNumber) { final String descendantName = TokenUtils.getTokenName(element); if (maximumMessage == null) { maximumMessage = MSG_KEY_MAX; } log(ast.getLineNo(), ast.getColumnNo(), maximumMessage, String.valueOf(tokenCount), String.valueOf(maximumNumber), name, descendantName); } } } /** * Log validation as one violation. * @param ast current token */ private void logAsTotal(DetailAST ast) { // name of this token final String name = TokenUtils.getTokenName(ast.getType()); int total = 0; for (int element : limitedTokens) { total += counts[element - 1]; } if (total < minimumNumber) { if (minimumMessage == null) { minimumMessage = MSG_KEY_SUM_MIN; } log(ast.getLineNo(), ast.getColumnNo(), minimumMessage, String.valueOf(total), String.valueOf(minimumNumber), name); } if (total > maximumNumber) { if (maximumMessage == null) { maximumMessage = MSG_KEY_SUM_MAX; } log(ast.getLineNo(), ast.getColumnNo(), maximumMessage, String.valueOf(total), String.valueOf(maximumNumber), name); } } /** * Counts the number of occurrences of descendant tokens. * @param ast the root token for descendants. * @param depth the maximum depth of the counted descendants. */ private void countTokens(AST ast, int depth) { if (depth <= maximumDepth) { //update count if (depth >= minimumDepth) { final int type = ast.getType(); if (type <= counts.length) { counts[type - 1]++; } } AST child = ast.getFirstChild(); final int nextDepth = depth + 1; while (child != null) { countTokens(child, nextDepth); child = child.getNextSibling(); } } } @Override public int[] getAcceptableTokens() { // Any tokens set by property 'tokens' are acceptable final Set tokenNames = getTokenNames(); final int[] result = new int[tokenNames.size()]; int index = 0; for (String name : tokenNames) { result[index] = TokenUtils.getTokenId(name); index++; } return result; } /** * Sets the tokens which occurrence as descendant is limited. * @param limitedTokensParam - list of tokens to ignore. */ public void setLimitedTokens(String... limitedTokensParam) { limitedTokens = new int[limitedTokensParam.length]; int maxToken = 0; for (int i = 0; i < limitedTokensParam.length; i++) { limitedTokens[i] = TokenUtils.getTokenId(limitedTokensParam[i]); if (limitedTokens[i] > maxToken) { maxToken = limitedTokens[i]; } } counts = new int[maxToken]; } /** * Sets the minimum depth for descendant counts. * @param minimumDepth the minimum depth for descendant counts. */ public void setMinimumDepth(int minimumDepth) { this.minimumDepth = minimumDepth; } /** * Sets the maximum depth for descendant counts. * @param maximumDepth the maximum depth for descendant counts. */ public void setMaximumDepth(int maximumDepth) { this.maximumDepth = maximumDepth; } /** * Sets a minimum count for descendants. * @param minimumNumber the minimum count for descendants. */ public void setMinimumNumber(int minimumNumber) { this.minimumNumber = minimumNumber; } /** * Sets a maximum count for descendants. * @param maximumNumber the maximum count for descendants. */ public void setMaximumNumber(int maximumNumber) { this.maximumNumber = maximumNumber; } /** * Sets the error message for minimum count not reached. * @param message the error message for minimum count not reached. * Used as a {@code MessageFormat} pattern with arguments *
    *
  • {0} - token count
  • *
  • {1} - minimum number
  • *
  • {2} - name of token
  • *
  • {3} - name of limited token
  • *
*/ public void setMinimumMessage(String message) { minimumMessage = message; } /** * Sets the error message for maximum count exceeded. * @param message the error message for maximum count exceeded. * Used as a {@code MessageFormat} pattern with arguments *
    *
  • {0} - token count
  • *
  • {1} - maximum number
  • *
  • {2} - name of token
  • *
  • {3} - name of limited token
  • *
*/ public void setMaximumMessage(String message) { maximumMessage = message; } /** * Sets whether to use the sum of the tokens found, rather than the * individual counts. * @param sum whether to use the sum. */ public void setSumTokenCounts(boolean sum) { sumTokenCounts = sum; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy