001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2018 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.CommonUtils; 029 030/** 031 * <p> 032 * Checks for illegal token text. 033 * </p> 034 * <p> An example of how to configure the check to forbid String literals 035 * containing {@code "a href"} is: 036 * </p> 037 * <pre> 038 * <module name="IllegalTokenText"> 039 * <property name="tokens" value="STRING_LITERAL"/> 040 * <property name="format" value="a href"/> 041 * </module> 042 * </pre> 043 * <p> An example of how to configure the check to forbid leading zeros in an 044 * integer literal, other than zero and a hex literal is: 045 * </p> 046 * <pre> 047 * <module name="IllegalTokenText"> 048 * <property name="tokens" value="NUM_INT,NUM_LONG"/> 049 * <property name="format" value="^0[^lx]"/> 050 * <property name="ignoreCase" value="true"/> 051 * </module> 052 * </pre> 053 * @author Rick Giles 054 */ 055@StatelessCheck 056public class IllegalTokenTextCheck 057 extends AbstractCheck { 058 059 /** 060 * A key is pointing to the warning message text in "messages.properties" 061 * file. 062 */ 063 public static final String MSG_KEY = "illegal.token.text"; 064 065 /** 066 * Custom message for report if illegal regexp found 067 * ignored if empty. 068 */ 069 private String message = ""; 070 071 /** The format string of the regexp. */ 072 private String formatString = "$^"; 073 074 /** The regexp to match against. */ 075 private Pattern format = Pattern.compile(formatString); 076 077 /** {@code true} if the casing should be ignored. */ 078 private boolean ignoreCase; 079 080 @Override 081 public int[] getDefaultTokens() { 082 return CommonUtils.EMPTY_INT_ARRAY; 083 } 084 085 @Override 086 public int[] getAcceptableTokens() { 087 return new int[] { 088 TokenTypes.NUM_DOUBLE, 089 TokenTypes.NUM_FLOAT, 090 TokenTypes.NUM_INT, 091 TokenTypes.NUM_LONG, 092 TokenTypes.IDENT, 093 TokenTypes.COMMENT_CONTENT, 094 TokenTypes.STRING_LITERAL, 095 TokenTypes.CHAR_LITERAL, 096 }; 097 } 098 099 @Override 100 public int[] getRequiredTokens() { 101 return CommonUtils.EMPTY_INT_ARRAY; 102 } 103 104 @Override 105 public boolean isCommentNodesRequired() { 106 return true; 107 } 108 109 @Override 110 public void visitToken(DetailAST ast) { 111 final String text = ast.getText(); 112 if (format.matcher(text).find()) { 113 String customMessage = message; 114 if (customMessage.isEmpty()) { 115 customMessage = MSG_KEY; 116 } 117 log( 118 ast.getLineNo(), 119 ast.getColumnNo(), 120 customMessage, 121 formatString); 122 } 123 } 124 125 /** 126 * Setter for message property. 127 * @param message custom message which should be used 128 * to report about violations. 129 */ 130 public void setMessage(String message) { 131 if (message == null) { 132 this.message = ""; 133 } 134 else { 135 this.message = message; 136 } 137 } 138 139 /** 140 * Set the format to the specified regular expression. 141 * @param format a {@code String} value 142 * @throws org.apache.commons.beanutils.ConversionException unable to parse format 143 */ 144 public void setFormat(String format) { 145 formatString = format; 146 updateRegexp(); 147 } 148 149 /** 150 * Set whether or not the match is case sensitive. 151 * @param caseInsensitive true if the match is case insensitive. 152 * @noinspection BooleanParameter 153 */ 154 public void setIgnoreCase(boolean caseInsensitive) { 155 ignoreCase = caseInsensitive; 156 updateRegexp(); 157 } 158 159 /** 160 * Updates the {@link #format} based on the values from {@link #formatString} and 161 * {@link #ignoreCase}. 162 */ 163 private void updateRegexp() { 164 final int compileFlags; 165 if (ignoreCase) { 166 compileFlags = Pattern.CASE_INSENSITIVE; 167 } 168 else { 169 compileFlags = 0; 170 } 171 format = CommonUtils.createPattern(formatString, compileFlags); 172 } 173 174}