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.naming;
020
021import java.util.regex.Pattern;
022
023import com.puppycrawl.tools.checkstyle.api.DetailAST;
024import com.puppycrawl.tools.checkstyle.api.ScopeUtils;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026import com.puppycrawl.tools.checkstyle.api.Utils;
027
028/**
029 * <p>
030 * Checks that local, non-final variable names conform to a format specified
031 * by the format property. A catch parameter is considered to be
032 * a local variable. The format is a
033 * {@link java.util.regex.Pattern regular expression}
034 * and defaults to
035 * <strong>^[a-z][a-zA-Z0-9]*$</strong>.
036 * </p>
037 * <p>
038 * An example of how to configure the check is:
039 * </p>
040 * <pre>
041 * &lt;module name="LocalVariableName"/&gt;
042 * </pre>
043 * <p>
044 * An example of how to configure the check for names that begin with a lower
045 * case letter, followed by letters, digits, and underscores is:
046 * </p>
047 * <pre>
048 * &lt;module name="LocalVariableName"&gt;
049 *    &lt;property name="format" value="^[a-z](_?[a-zA-Z0-9]+)*$"/&gt;
050 * &lt;/module&gt;
051 * </pre>
052 * <p>
053 * An example of one character variable name in
054 * initialization expression(like "i") in FOR loop:
055 * </p>
056 * <pre>
057 * for(int i = 1; i &lt; 10; i++) {}
058 * </pre>
059 * <p>
060 * An example of how to configure the check to allow one char variable name in
061 * <a href="http://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html">
062 * initialization expressions</a> in FOR loop:
063 * </p>
064 * <pre>
065 * &lt;module name="LocalVariableName"&gt;
066 *    &lt;property name="allowOneCharVarInForLoop" value="true"/&gt;
067 * &lt;/module&gt;
068 * </pre>
069 * <p>
070 *
071 * @author Rick Giles
072 * @author maxvetrenko
073 * @version 1.0
074 */
075public class LocalVariableNameCheck
076    extends AbstractNameCheck
077{
078    /**
079     * Allow one character name for initialization expression in FOR loop.
080     */
081    private boolean allowOneCharVarInForLoop;
082
083    /** Regexp for one-char loop variables. */
084    private static Pattern sSingleChar = Utils.getPattern("^[a-z]$");
085
086    /** Creates a new <code>LocalVariableNameCheck</code> instance. */
087    public LocalVariableNameCheck()
088    {
089        super("^[a-z][a-zA-Z0-9]*$");
090    }
091
092    public final void setAllowOneCharVarInForLoop(boolean allow)
093    {
094        allowOneCharVarInForLoop = allow;
095    }
096
097    @Override
098    public int[] getDefaultTokens()
099    {
100        return new int[] {
101            TokenTypes.VARIABLE_DEF,
102            TokenTypes.PARAMETER_DEF,
103        };
104    }
105
106    @Override
107    protected final boolean mustCheckName(DetailAST ast)
108    {
109        final DetailAST modifiersAST =
110            ast.findFirstToken(TokenTypes.MODIFIERS);
111        final boolean isFinal = (modifiersAST != null)
112            && modifiersAST.branchContains(TokenTypes.FINAL);
113        if (allowOneCharVarInForLoop && isForLoopVariable(ast)) {
114            final String variableName =
115                    ast.findFirstToken(TokenTypes.IDENT).getText();
116            return !sSingleChar.matcher(variableName).find();
117        }
118        return (!isFinal && ScopeUtils.isLocalVariableDef(ast));
119    }
120
121    /**
122     * Checks if a variable is the loop's one.
123     * @param variableDef variable definition.
124     * @return true if a variable is the loop's one.
125     */
126    private boolean isForLoopVariable(DetailAST variableDef)
127    {
128        final int parentType = variableDef.getParent().getType();
129        return  parentType == TokenTypes.FOR_INIT
130                || parentType == TokenTypes.FOR_EACH_CLAUSE;
131    }
132}