diff --git a/contrib/examples/checks/com/puppycrawl/tools/checkstyle/checks/coding/DescendantTokenCheck.java b/contrib/examples/checks/com/puppycrawl/tools/checkstyle/checks/coding/DescendantTokenCheck.java new file mode 100644 index 000000000..3b8e3f193 --- /dev/null +++ b/contrib/examples/checks/com/puppycrawl/tools/checkstyle/checks/coding/DescendantTokenCheck.java @@ -0,0 +1,367 @@ +//////////////////////////////////////////////////////////////////////////////// +// checkstyle: Checks Java source code for adherence to a set of rules. +// Copyright (C) 2001-2003 Oliver Burn +// +// 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.coding; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Set; + +import antlr.collections.AST; + +import com.puppycrawl.tools.checkstyle.api.Check; +import com.puppycrawl.tools.checkstyle.api.DetailAST; +import com.puppycrawl.tools.checkstyle.api.TokenTypes; + +/** + *
+ * 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"/> + * </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> + * + * <!-- Initialiser 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 <tim@tt1.org>
+ * @author Rick Giles
+ */
+public class DescendantTokenCheck extends Check
+{
+ /** minimum depth */
+ private int mMinimumDepth = 0;
+
+ /** maximum depth */
+ private int mMaximumDepth = Integer.MAX_VALUE;
+
+ /** minimum number */
+ private int mMinimumNumber = 0;
+
+ /** maximum number */
+ private int mMaximumNumber = Integer.MAX_VALUE;
+
+ /** limited tokens */
+ private int[] mLimitedTokens = new int[0];
+
+ /** error message when minimum count not reached */
+ private String mMinimumMessage = "descendant.token.min";
+
+ /** error message when maximum count exceeded */
+ private String mMaximumMessage = "descendant.token.max";
+
+ /**
+ * Counts of descendant tokens.
+ * Indexed by (token ID - 1) for performance.
+ */
+ private int[] mCounts = new int[0];
+
+ /** @see com.puppycrawl.tools.checkstyle.api.Check#getDefaultTokens() */
+ public int[] getDefaultTokens()
+ {
+ return new int[0];
+ }
+
+ /** @see com.puppycrawl.tools.checkstyle.api.Check */
+ public void visitToken(DetailAST aAST)
+ {
+ //reset counts
+ Arrays.fill(mCounts, 0);
+
+ countTokens(aAST, 0);
+
+ // name of this token
+ final String name = TokenTypes.getTokenName(aAST.getType());
+
+ for (int i = 0; i < mLimitedTokens.length; i++) {
+ final int tokenCount = mCounts[mLimitedTokens[i] - 1];
+ if (tokenCount < mMinimumNumber) {
+ final String descendantName =
+ TokenTypes.getTokenName(mLimitedTokens[i]);
+ log(
+ aAST.getLineNo(),
+ aAST.getColumnNo(),
+ mMinimumMessage,
+ new String[] {
+ "" + tokenCount,
+ "" + mMinimumNumber,
+ name,
+ descendantName,
+ });
+ }
+ if (tokenCount > mMaximumNumber) {
+ final String descendantName =
+ TokenTypes.getTokenName(mLimitedTokens[i]);
+ log(
+ aAST.getLineNo(),
+ aAST.getColumnNo(),
+ mMaximumMessage,
+ new String[] {
+ "" + tokenCount,
+ "" + mMaximumNumber,
+ name,
+ descendantName,
+ });
+ }
+ }
+ }
+
+ /**
+ * Counts the number of occurrences of descendant tokens.
+ * @param aAST the root token for descendants.
+ * @param aDepth the maximum depth of the counted descendants.
+ */
+ private void countTokens(AST aAST, int aDepth)
+ {
+ if (aDepth <= mMaximumDepth) {
+ //update count
+ if (aDepth >= mMinimumDepth) {
+ final int type = aAST.getType();
+ if (type <= mCounts.length) {
+ mCounts[type - 1]++;
+ }
+ }
+ AST child = aAST.getFirstChild();
+ final int nextDepth = aDepth + 1;
+ while (child != null) {
+ countTokens(child, nextDepth);
+ child = child.getNextSibling();
+ }
+ }
+ }
+
+ /** @see com.puppycrawl.tools.checkstyle.api.Check */
+ public int[] getAcceptableTokens()
+ {
+ // Any tokens set by property 'tokens' are acceptable
+ final Set tokenNames = getTokenNames();
+ final int[] result = new int[tokenNames.size()];
+ int i = 0;
+ final Iterator it = tokenNames.iterator();
+ while (it.hasNext()) {
+ final String name = (String) it.next();
+ result[i] = TokenTypes.getTokenId(name);
+ i++;
+ }
+ return result;
+ }
+
+ /**
+ * Sets the tokens to ignore in the check.
+ * @param aLimitedTokens - list of tokens to ignore.
+ */
+ public void setLimitedTokens(String[] aLimitedTokens)
+ {
+ mLimitedTokens = new int[aLimitedTokens.length];
+
+ int maxToken = 0;
+ for (int i = 0; i < aLimitedTokens.length; i++) {
+ mLimitedTokens[i] = TokenTypes.getTokenId(aLimitedTokens[i]);
+ if (mLimitedTokens[i] > maxToken) {
+ maxToken = mLimitedTokens[i];
+ }
+ }
+ mCounts = new int[maxToken];
+ }
+
+ /**
+ * Sets the mimimum depth for descendant counts.
+ * @param aMinimumDepth the mimimum depth for descendant counts.
+ */
+ public void setMinimumDepth(int aMinimumDepth)
+ {
+ mMinimumDepth = aMinimumDepth;
+ }
+
+ /**
+ * Sets the maximum depth for descendant counts.
+ * @param aMaximumDepth the maximum depth for descendant counts.
+ */
+ public void setMaximumDepth(int aMaximumDepth)
+ {
+ mMaximumDepth = aMaximumDepth;
+ }
+
+ /**
+ * Sets a minimum count for descendants.
+ * @param aMinimumNumber the minimum count for descendants.
+ */
+ public void setMinimumNumber(int aMinimumNumber)
+ {
+ mMinimumNumber = aMinimumNumber;
+ }
+
+ /**
+ * Sets a maximum count for descendants.
+ * @param aMaximumNumber the maximum count for descendants.
+ */
+ public void setMaximumNumber(int aMaximumNumber)
+ {
+ mMaximumNumber = aMaximumNumber;
+ }
+
+ /**
+ * Sets the error message for minimum count not reached.
+ * @param aMessage the error message for minimum count not reached.
+ * Used as a MessageFormat pattern with arguments
+ *
MessageFormat pattern with arguments
+ *