1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.naming;
21  
22  import java.util.regex.Pattern;
23  
24  import com.puppycrawl.tools.checkstyle.api.DetailAST;
25  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26  import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
27  
28  /**
29   * <p>
30   * Checks that local, non-{@code final} variable names conform to a format specified
31   * by the format property. A catch parameter is considered to be
32   * a local variable.
33   * </p>
34   * <ul>
35   * <li>
36   * Property {@code format} - Specifies valid identifiers. Default value is
37   * {@code "^[a-z][a-zA-Z0-9]*$"}.
38   * </li>
39   * <li>
40   * Property {@code allowOneCharVarInForLoop} - Allow one character variable name in
41   * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html">initialization expressions</a>
42   * in FOR loop. For example:
43   * <pre>
44   * for (int i = 1; i &lt; 10; i++) {}
45   * </pre>
46   * Default value is {@code false}.
47   * </li>
48   * </ul>
49   * <p>
50   * An example of how to configure the check is:
51   * </p>
52   * <pre>
53   * &lt;module name="LocalVariableName"/&gt;
54   * </pre>
55   * <p>Code Example:</p>
56   * <pre>
57   * class MyClass {
58   *   void MyMethod() {
59   *     for (int var = 1; var &lt; 10; var++) {} // OK
60   *     for (int VAR = 1; VAR &lt; 10; VAR++) {} // violation, name 'VAR' must match
61   *                                           // pattern '^[a-z][a-zA-Z0-9]*$'
62   *     for (int i = 1; i &lt; 10; i++) {} // OK
63   *     for (int var_1 = 0; var_1 &lt; 10; var_1++) {} // violation, name 'var_1' must match
64   *                                                    // pattern '^[a-z][a-zA-Z0-9]*$'
65   *   }
66   * }
67   * </pre>
68   * <p>
69   * An example of how to configure the check for names that begin with a lower
70   * case letter, followed by letters, digits, and underscores is:
71   * </p>
72   * <pre>
73   * &lt;module name="LocalVariableName"&gt;
74   *   &lt;property name="format" value="^[a-z](_?[a-zA-Z0-9]+)*$"/&gt;
75   * &lt;/module&gt;
76   * </pre>
77   * <p>Code Example:</p>
78   * <pre>
79   * class MyClass {
80   *   void MyMethod() {
81   *     for (int var = 1; var &lt; 10; var++) {} // OK
82   *     for (int VAR = 1; VAR &lt; 10; VAR++) {} // violation, name 'VAR' must match
83   *                                              // pattern '^[a-z](_?[a-zA-Z0-9]+)*$'
84   *     for (int i = 1; i &lt; 10; i++) {} // OK
85   *     for (int var_1 = 0; var_1 &lt; 10; var_1++) {} // OK
86   *   }
87   * }
88   * </pre>
89   * <p>
90   * An example of one character variable name in
91   * initialization expression(like "i") in FOR loop:
92   * </p>
93   * <pre>
94   * for(int i = 1; i &lt; 10; i++) {}
95   * </pre>
96   * <p>
97   * An example of how to configure the check to allow one character variable name in
98   * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html">
99   * initialization expressions</a> in FOR loop:
100  * </p>
101  * <pre>
102  * &lt;module name="LocalVariableName"&gt;
103  *   &lt;property name="allowOneCharVarInForLoop" value="true"/&gt;
104  * &lt;/module&gt;
105  * </pre>
106  *
107  * @since 3.0
108  */
109 public class LocalVariableNameCheck
110     extends AbstractNameCheck {
111 
112     /** Regexp for one-char loop variables. */
113     private static final Pattern SINGLE_CHAR = Pattern.compile("^[a-z]$");
114 
115     /**
116      * Allow one character variable name in
117      * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html">initialization expressions</a>
118      * in FOR loop. For example:
119      * <pre>
120      * for (int i = 1; i &lt; 10; i++) {}
121      * </pre>
122      */
123     private boolean allowOneCharVarInForLoop;
124 
125     /** Creates a new {@code LocalVariableNameCheck} instance. */
126     public LocalVariableNameCheck() {
127         super("^[a-z][a-zA-Z0-9]*$");
128     }
129 
130     /**
131      * Setter to allow one character variable name in
132      * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html">initialization expressions</a>
133      * in FOR loop. For example:
134      * <pre>
135      * for (int i = 1; i &lt; 10; i++) {}
136      * </pre>
137      *
138      * @param allow Flag for allowing or not one character name in FOR loop.
139      */
140     public final void setAllowOneCharVarInForLoop(boolean allow) {
141         allowOneCharVarInForLoop = allow;
142     }
143 
144     @Override
145     public int[] getDefaultTokens() {
146         return getRequiredTokens();
147     }
148 
149     @Override
150     public int[] getAcceptableTokens() {
151         return getRequiredTokens();
152     }
153 
154     @Override
155     public int[] getRequiredTokens() {
156         return new int[] {
157             TokenTypes.VARIABLE_DEF,
158         };
159     }
160 
161     @Override
162     protected final boolean mustCheckName(DetailAST ast) {
163         final boolean result;
164         if (allowOneCharVarInForLoop && isForLoopVariable(ast)) {
165             final String variableName = ast.findFirstToken(TokenTypes.IDENT).getText();
166             result = !SINGLE_CHAR.matcher(variableName).find();
167         }
168         else {
169             final DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS);
170             final boolean isFinal = modifiersAST.findFirstToken(TokenTypes.FINAL) != null;
171             result = !isFinal && ScopeUtil.isLocalVariableDef(ast);
172         }
173         return result;
174     }
175 
176     /**
177      * Checks if a variable is the loop's one.
178      * @param variableDef variable definition.
179      * @return true if a variable is the loop's one.
180      */
181     private static boolean isForLoopVariable(DetailAST variableDef) {
182         final int parentType = variableDef.getParent().getType();
183         return parentType == TokenTypes.FOR_INIT
184                 || parentType == TokenTypes.FOR_EACH_CLAUSE;
185     }
186 
187 }