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.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 from {@code format} property. 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> 064 * To configure the check to forbid leading zeros in an integer literal, 065 * other than zero and a hex literal: 066 * </p> 067 * <pre> 068 * <module name="IllegalTokenText"> 069 * <property name="tokens" value="NUM_INT,NUM_LONG"/> 070 * <property name="format" value="^0[^lx]"/> 071 * <property name="ignoreCase" value="true"/> 072 * </module> 073 * </pre> 074 * 075 * @since 3.2 076 */ 077@StatelessCheck 078public class IllegalTokenTextCheck 079 extends AbstractCheck { 080 081 /** 082 * A key is pointing to the warning message text in "messages.properties" 083 * file. 084 */ 085 public static final String MSG_KEY = "illegal.token.text"; 086 087 /** 088 * Define the message which is used to notify about violations; 089 * if empty then the default message is used. 090 */ 091 private String message = ""; 092 093 /** The format string of the regexp. */ 094 private String formatString = "^$"; 095 096 /** Define the RegExp for illegal pattern. */ 097 private Pattern format = Pattern.compile(formatString); 098 099 /** Control whether to ignore case when matching. */ 100 private boolean ignoreCase; 101 102 @Override 103 public int[] getDefaultTokens() { 104 return CommonUtil.EMPTY_INT_ARRAY; 105 } 106 107 @Override 108 public int[] getAcceptableTokens() { 109 return new int[] { 110 TokenTypes.NUM_DOUBLE, 111 TokenTypes.NUM_FLOAT, 112 TokenTypes.NUM_INT, 113 TokenTypes.NUM_LONG, 114 TokenTypes.IDENT, 115 TokenTypes.COMMENT_CONTENT, 116 TokenTypes.STRING_LITERAL, 117 TokenTypes.CHAR_LITERAL, 118 }; 119 } 120 121 @Override 122 public int[] getRequiredTokens() { 123 return CommonUtil.EMPTY_INT_ARRAY; 124 } 125 126 @Override 127 public boolean isCommentNodesRequired() { 128 return true; 129 } 130 131 @Override 132 public void visitToken(DetailAST ast) { 133 final String text = ast.getText(); 134 if (format.matcher(text).find()) { 135 String customMessage = message; 136 if (customMessage.isEmpty()) { 137 customMessage = MSG_KEY; 138 } 139 log( 140 ast, 141 customMessage, 142 formatString); 143 } 144 } 145 146 /** 147 * Setter to define the message which is used to notify about violations; 148 * if empty then the default message is used. 149 * @param message custom message which should be used 150 * to report about violations. 151 */ 152 public void setMessage(String message) { 153 if (message == null) { 154 this.message = ""; 155 } 156 else { 157 this.message = message; 158 } 159 } 160 161 /** 162 * Setter to define the RegExp for illegal pattern. 163 * @param format a {@code String} value 164 */ 165 public void setFormat(String format) { 166 formatString = format; 167 updateRegexp(); 168 } 169 170 /** 171 * Setter to control whether to ignore case when matching. 172 * @param caseInsensitive true if the match is case insensitive. 173 */ 174 public void setIgnoreCase(boolean caseInsensitive) { 175 ignoreCase = caseInsensitive; 176 updateRegexp(); 177 } 178 179 /** 180 * Updates the {@link #format} based on the values from {@link #formatString} and 181 * {@link #ignoreCase}. 182 */ 183 private void updateRegexp() { 184 final int compileFlags; 185 if (ignoreCase) { 186 compileFlags = Pattern.CASE_INSENSITIVE; 187 } 188 else { 189 compileFlags = 0; 190 } 191 format = CommonUtil.createPattern(formatString, compileFlags); 192 } 193 194}