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 com.puppycrawl.tools.checkstyle.api.Check;
022import com.puppycrawl.tools.checkstyle.api.DetailAST;
023import com.puppycrawl.tools.checkstyle.api.TokenTypes;
024
025/**
026 * Check that method/constructor/catch/foreach parameters are final.
027 * The user can set the token set to METHOD_DEF, CONSTRUCTOR_DEF,
028 * LITERAL_CATCH, FOR_EACH_CLAUSE or any combination of these token
029 * types, to control the scope of this check.
030 * Default scope is both METHOD_DEF and CONSTRUCTOR_DEF.
031 *
032 * @author lkuehne
033 * @author o_sukhodolsky
034 * @author Michael Studman
035 */
036public class FinalParametersCheck extends Check
037{
038    @Override
039    public int[] getDefaultTokens()
040    {
041        return new int[] {
042            TokenTypes.METHOD_DEF,
043            TokenTypes.CTOR_DEF,
044        };
045    }
046
047    @Override
048    public int[] getAcceptableTokens()
049    {
050        return new int[] {
051            TokenTypes.METHOD_DEF,
052            TokenTypes.CTOR_DEF,
053            TokenTypes.LITERAL_CATCH,
054            TokenTypes.FOR_EACH_CLAUSE,
055        };
056    }
057
058    @Override
059    public void visitToken(DetailAST aAST)
060    {
061        // don't flag interfaces
062        final DetailAST container = aAST.getParent().getParent();
063        if (container.getType() == TokenTypes.INTERFACE_DEF) {
064            return;
065        }
066
067        if (aAST.getType() == TokenTypes.LITERAL_CATCH) {
068            visitCatch(aAST);
069        }
070        else if (aAST.getType() == TokenTypes.FOR_EACH_CLAUSE) {
071            visitForEachClause(aAST);
072        }
073        else {
074            visitMethod(aAST);
075        }
076    }
077
078    /**
079     * Checks parameters of the method or ctor.
080     * @param aMethod method or ctor to check.
081     */
082    private void visitMethod(final DetailAST aMethod)
083    {
084        // exit on fast lane if there is nothing to check here
085        if (!aMethod.branchContains(TokenTypes.PARAMETER_DEF)) {
086            return;
087        }
088
089        // ignore abstract method
090        final DetailAST modifiers =
091            aMethod.findFirstToken(TokenTypes.MODIFIERS);
092        if (modifiers.branchContains(TokenTypes.ABSTRACT)) {
093            return;
094        }
095
096        // we can now be sure that there is at least one parameter
097        final DetailAST parameters =
098            aMethod.findFirstToken(TokenTypes.PARAMETERS);
099        DetailAST child = parameters.getFirstChild();
100        while (child != null) {
101            // childs are PARAMETER_DEF and COMMA
102            if (child.getType() == TokenTypes.PARAMETER_DEF) {
103                checkParam(child);
104            }
105            child = child.getNextSibling();
106        }
107    }
108
109    /**
110     * Checks parameter of the catch block.
111     * @param aCatch catch block to check.
112     */
113    private void visitCatch(final DetailAST aCatch)
114    {
115        checkParam(aCatch.findFirstToken(TokenTypes.PARAMETER_DEF));
116    }
117
118    /**
119     * Checks parameter of the for each clause.
120     * @param aForEachClause for each clause to check.
121     */
122    private void visitForEachClause(final DetailAST aForEachClause)
123    {
124        checkParam(aForEachClause.findFirstToken(TokenTypes.VARIABLE_DEF));
125    }
126
127    /**
128     * Checks if the given parameter is final.
129     * @param aParam parameter to check.
130     */
131    private void checkParam(final DetailAST aParam)
132    {
133        if (!aParam.branchContains(TokenTypes.FINAL)) {
134            final DetailAST paramName = aParam.findFirstToken(TokenTypes.IDENT);
135            final DetailAST firstNode = CheckUtils.getFirstNode(aParam);
136            log(firstNode.getLineNo(), firstNode.getColumnNo(),
137                "final.parameter", paramName.getText());
138        }
139    }
140}