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 < 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 * <module name="LocalVariableName"/> 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 * <module name="LocalVariableName"> 061 * <property name="format" value="^[a-z](_?[a-zA-Z0-9]+)*$"/> 062 * </module> 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 < 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 * <module name="LocalVariableName"> 078 * <property name="allowOneCharVarInForLoop" value="true"/> 079 * </module> 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 < 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 < 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}