001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2019 the original author or authors.
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////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.naming;
021
022import java.util.regex.Pattern;
023
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
027
028/**
029 * <p>
030 * Checks that local, non-{@code final} variable names conform to a format specified
031 * by the format property. A catch parameter is considered to be
032 * a local variable.
033 * </p>
034 * <ul>
035 * <li>
036 * Property {@code format} - Specifies valid identifiers. Default value is
037 * {@code "^[a-z][a-zA-Z0-9]*$"}.
038 * </li>
039 * <li>
040 * Property {@code allowOneCharVarInForLoop} - Allow one character variable name in
041 * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html">initialization expressions</a>
042 * in FOR loop. For example:
043 * <pre>
044 * for (int i = 1; i &lt; 10; i++) {}
045 * </pre>
046 * Default value is {@code false}.
047 * </li>
048 * </ul>
049 * <p>
050 * An example of how to configure the check is:
051 * </p>
052 * <pre>
053 * &lt;module name="LocalVariableName"/&gt;
054 * </pre>
055 * <p>
056 * An example of how to configure the check for names that begin with a lower
057 * case letter, followed by letters, digits, and underscores is:
058 * </p>
059 * <pre>
060 * &lt;module name="LocalVariableName"&gt;
061 *   &lt;property name="format" value="^[a-z](_?[a-zA-Z0-9]+)*$"/&gt;
062 * &lt;/module&gt;
063 * </pre>
064 * <p>
065 * An example of one character variable name in
066 * initialization expression(like "i") in FOR loop:
067 * </p>
068 * <pre>
069 * for(int i = 1; i &lt; 10; i++) {}
070 * </pre>
071 * <p>
072 * An example of how to configure the check to allow one character variable name in
073 * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html">
074 * initialization expressions</a> in FOR loop:
075 * </p>
076 * <pre>
077 * &lt;module name="LocalVariableName"&gt;
078 *   &lt;property name="allowOneCharVarInForLoop" value="true"/&gt;
079 * &lt;/module&gt;
080 * </pre>
081 *
082 * @since 3.0
083 */
084public class LocalVariableNameCheck
085    extends AbstractNameCheck {
086
087    /** Regexp for one-char loop variables. */
088    private static final Pattern SINGLE_CHAR = Pattern.compile("^[a-z]$");
089
090    /**
091     * Allow one character variable name in
092     * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html">initialization expressions</a>
093     * in FOR loop. For example:
094     * <pre>
095     * for (int i = 1; i &lt; 10; i++) {}
096     * </pre>
097     */
098    private boolean allowOneCharVarInForLoop;
099
100    /** Creates a new {@code LocalVariableNameCheck} instance. */
101    public LocalVariableNameCheck() {
102        super("^[a-z][a-zA-Z0-9]*$");
103    }
104
105    /**
106     * Setter to allow one character variable name in
107     * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html">initialization expressions</a>
108     * in FOR loop. For example:
109     * <pre>
110     * for (int i = 1; i &lt; 10; i++) {}
111     * </pre>
112     *
113     * @param allow Flag for allowing or not one character name in FOR loop.
114     */
115    public final void setAllowOneCharVarInForLoop(boolean allow) {
116        allowOneCharVarInForLoop = allow;
117    }
118
119    @Override
120    public int[] getDefaultTokens() {
121        return getRequiredTokens();
122    }
123
124    @Override
125    public int[] getAcceptableTokens() {
126        return getRequiredTokens();
127    }
128
129    @Override
130    public int[] getRequiredTokens() {
131        return new int[] {
132            TokenTypes.VARIABLE_DEF,
133        };
134    }
135
136    @Override
137    protected final boolean mustCheckName(DetailAST ast) {
138        final boolean result;
139        if (allowOneCharVarInForLoop && isForLoopVariable(ast)) {
140            final String variableName = ast.findFirstToken(TokenTypes.IDENT).getText();
141            result = !SINGLE_CHAR.matcher(variableName).find();
142        }
143        else {
144            final DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS);
145            final boolean isFinal = modifiersAST.findFirstToken(TokenTypes.FINAL) != null;
146            result = !isFinal && ScopeUtil.isLocalVariableDef(ast);
147        }
148        return result;
149    }
150
151    /**
152     * Checks if a variable is the loop's one.
153     * @param variableDef variable definition.
154     * @return true if a variable is the loop's one.
155     */
156    private static boolean isForLoopVariable(DetailAST variableDef) {
157        final int parentType = variableDef.getParent().getType();
158        return parentType == TokenTypes.FOR_INIT
159                || parentType == TokenTypes.FOR_EACH_CLAUSE;
160    }
161
162}