001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 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.coding; 021 022import java.util.regex.Pattern; 023 024import com.puppycrawl.tools.checkstyle.StatelessCheck; 025import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 026import com.puppycrawl.tools.checkstyle.api.DetailAST; 027import com.puppycrawl.tools.checkstyle.api.TokenTypes; 028import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 029 030/** 031 * <p> 032 * Checks specified tokens text for matching an illegal pattern. 033 * By default no tokens are specified. 034 * </p> 035 * <ul> 036 * <li> 037 * Property {@code format} - Define the RegExp for illegal pattern. 038 * Default value is {@code "^$" (empty)}. 039 * </li> 040 * <li> 041 * Property {@code ignoreCase} - Control whether to ignore case when matching. 042 * Default value is {@code false}. 043 * </li> 044 * <li> 045 * Property {@code message} - Define the message which is used to notify about violations; 046 * if empty then the default message is used. 047 * Default value is {@code ""}. 048 * </li> 049 * <li> 050 * Property {@code tokens} - tokens to check 051 * Default value is: empty. 052 * </li> 053 * </ul> 054 * <p> 055 * To configure the check to forbid String literals containing {@code "a href"}: 056 * </p> 057 * <pre> 058 * <module name="IllegalTokenText"> 059 * <property name="tokens" value="STRING_LITERAL"/> 060 * <property name="format" value="a href"/> 061 * </module> 062 * </pre> 063 * <p>Example:</p> 064 * <pre> 065 * public void myTest() { 066 * String test = "a href"; // violation 067 * String test2 = "A href"; // OK, case is sensitive 068 * } 069 * </pre> 070 * <p> 071 * To configure the check to forbid String literals containing {@code "a href"} 072 * for the ignoreCase mode: 073 * </p> 074 * <pre> 075 * <module name="IllegalTokenText"> 076 * <property name="tokens" value="STRING_LITERAL"/> 077 * <property name="format" value="a href"/> 078 * <property name="ignoreCase" value="true"/> 079 * </module> 080 * </pre> 081 * <p>Example:</p> 082 * <pre> 083 * public void myTest() { 084 * String test = "a href"; // violation 085 * String test2 = "A href"; // violation, case is ignored 086 * } 087 * </pre> 088 * <p> 089 * To configure the check to forbid leading zeros in an integer literal, 090 * other than zero and a hex literal: 091 * </p> 092 * <pre> 093 * <module name="IllegalTokenText"> 094 * <property name="tokens" value="NUM_INT,NUM_LONG"/> 095 * <property name="format" value="^0[^lx]"/> 096 * <property name="ignoreCase" value="true"/> 097 * </module> 098 * </pre> 099 * <p>Example:</p> 100 * <pre> 101 * public void myTest() { 102 * int test1 = 0; // OK 103 * int test2 = 0x111; // OK 104 * int test3 = 0X111; // OK, case is ignored 105 * int test4 = 010; // violation 106 * long test5 = 0L; // OK 107 * long test6 = 010L; // violation 108 * } 109 * </pre> 110 * 111 * @since 3.2 112 */ 113@StatelessCheck 114public class IllegalTokenTextCheck 115 extends AbstractCheck { 116 117 /** 118 * A key is pointing to the warning message text in "messages.properties" 119 * file. 120 */ 121 public static final String MSG_KEY = "illegal.token.text"; 122 123 /** 124 * Define the message which is used to notify about violations; 125 * if empty then the default message is used. 126 */ 127 private String message = ""; 128 129 /** The format string of the regexp. */ 130 private String formatString = "^$"; 131 132 /** Define the RegExp for illegal pattern. */ 133 private Pattern format = Pattern.compile(formatString); 134 135 /** Control whether to ignore case when matching. */ 136 private boolean ignoreCase; 137 138 @Override 139 public int[] getDefaultTokens() { 140 return CommonUtil.EMPTY_INT_ARRAY; 141 } 142 143 @Override 144 public int[] getAcceptableTokens() { 145 return new int[] { 146 TokenTypes.NUM_DOUBLE, 147 TokenTypes.NUM_FLOAT, 148 TokenTypes.NUM_INT, 149 TokenTypes.NUM_LONG, 150 TokenTypes.IDENT, 151 TokenTypes.COMMENT_CONTENT, 152 TokenTypes.STRING_LITERAL, 153 TokenTypes.CHAR_LITERAL, 154 }; 155 } 156 157 @Override 158 public int[] getRequiredTokens() { 159 return CommonUtil.EMPTY_INT_ARRAY; 160 } 161 162 @Override 163 public boolean isCommentNodesRequired() { 164 return true; 165 } 166 167 @Override 168 public void visitToken(DetailAST ast) { 169 final String text = ast.getText(); 170 if (format.matcher(text).find()) { 171 String customMessage = message; 172 if (customMessage.isEmpty()) { 173 customMessage = MSG_KEY; 174 } 175 log( 176 ast, 177 customMessage, 178 formatString); 179 } 180 } 181 182 /** 183 * Setter to define the message which is used to notify about violations; 184 * if empty then the default message is used. 185 * 186 * @param message custom message which should be used 187 * to report about violations. 188 */ 189 public void setMessage(String message) { 190 if (message == null) { 191 this.message = ""; 192 } 193 else { 194 this.message = message; 195 } 196 } 197 198 /** 199 * Setter to define the RegExp for illegal pattern. 200 * 201 * @param format a {@code String} value 202 */ 203 public void setFormat(String format) { 204 formatString = format; 205 updateRegexp(); 206 } 207 208 /** 209 * Setter to control whether to ignore case when matching. 210 * 211 * @param caseInsensitive true if the match is case insensitive. 212 */ 213 public void setIgnoreCase(boolean caseInsensitive) { 214 ignoreCase = caseInsensitive; 215 updateRegexp(); 216 } 217 218 /** 219 * Updates the {@link #format} based on the values from {@link #formatString} and 220 * {@link #ignoreCase}. 221 */ 222 private void updateRegexp() { 223 final int compileFlags; 224 if (ignoreCase) { 225 compileFlags = Pattern.CASE_INSENSITIVE; 226 } 227 else { 228 compileFlags = 0; 229 } 230 format = CommonUtil.createPattern(formatString, compileFlags); 231 } 232 233}