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.design;
020
021import com.puppycrawl.tools.checkstyle.api.Check;
022import com.puppycrawl.tools.checkstyle.api.DetailAST;
023import com.puppycrawl.tools.checkstyle.api.TokenTypes;
024
025/**
026 * <p>
027 * Restricts throws statements to a specified count (default = 1).
028 * </p>
029 * <p>
030 * Rationale:
031 * Exceptions form part of a methods interface. Declaring
032 * a method to throw too many differently rooted
033 * exceptions makes exception handling onerous and leads
034 * to poor programming practices such as catch
035 * (Exception). This check forces developers to put
036 * exceptions into a hierarchy such that in the simplest
037 * case, only one type of exception need be checked for by
038 * a caller but allows any sub-classes to be caught
039 * specifically if necessary.
040 * </p>
041 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a>
042 */
043public final class ThrowsCountCheck extends Check
044{
045    /** default value of max property */
046    private static final int DEFAULT_MAX = 1;
047
048    /** maximum allowed throws statements */
049    private int max;
050
051    /** Creates new instance of the check. */
052    public ThrowsCountCheck()
053    {
054        setMax(DEFAULT_MAX);
055    }
056
057    @Override
058    public int[] getDefaultTokens()
059    {
060        return new int[] {
061            TokenTypes.LITERAL_THROWS,
062        };
063    }
064
065    @Override
066    public int[] getRequiredTokens()
067    {
068        return getDefaultTokens();
069    }
070
071    /**
072     * Getter for max property.
073     * @return maximum allowed throws statements.
074     */
075    public int getMax()
076    {
077        return max;
078    }
079
080    /**
081     * Setter for max property.
082     * @param max maximum allowed throws statements.
083     */
084    public void setMax(int max)
085    {
086        this.max = max;
087    }
088
089    @Override
090    public void visitToken(DetailAST ast)
091    {
092        switch (ast.getType()) {
093            case TokenTypes.LITERAL_THROWS:
094                visitLiteralThrows(ast);
095                break;
096            default:
097                throw new IllegalStateException(ast.toString());
098        }
099    }
100
101    /**
102     * Checks number of throws statements.
103     * @param ast throws for check.
104     */
105    private void visitLiteralThrows(DetailAST ast)
106    {
107        // Account for all the commas!
108        final int count = (ast.getChildCount() + 1) / 2;
109        if (count > getMax()) {
110            log(ast.getLineNo(),  ast.getColumnNo(), "throws.count",
111                count, getMax());
112        }
113    }
114}