001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2023 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.Objects;
023import java.util.regex.Pattern;
024
025import com.puppycrawl.tools.checkstyle.StatelessCheck;
026import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
027import com.puppycrawl.tools.checkstyle.api.DetailAST;
028import com.puppycrawl.tools.checkstyle.api.TokenTypes;
029import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
030
031/**
032 * <p>
033 * Checks specified tokens text for matching an illegal pattern.
034 * By default, no tokens are specified.
035 * </p>
036 * <ul>
037 * <li>
038 * Property {@code format} - Define the RegExp for illegal pattern.
039 * Type is {@code java.util.regex.Pattern}.
040 * Default value is {@code "^$"}.
041 * </li>
042 * <li>
043 * Property {@code ignoreCase} - Control whether to ignore case when matching.
044 * Type is {@code boolean}.
045 * Default value is {@code false}.
046 * </li>
047 * <li>
048 * Property {@code message} - Define the message which is used to notify about violations;
049 * if empty then the default message is used.
050 * Type is {@code java.lang.String}.
051 * Default value is {@code ""}.
052 * </li>
053 * <li>
054 * Property {@code tokens} - tokens to check
055 * Type is {@code java.lang.String[]}.
056 * Validation type is {@code tokenSet}.
057 * Default value is: {@code ""}.
058 * </li>
059 * </ul>
060 * <p>
061 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
062 * </p>
063 * <p>
064 * Violation Message Keys:
065 * </p>
066 * <ul>
067 * <li>
068 * {@code illegal.token.text}
069 * </li>
070 * </ul>
071 *
072 * @since 3.2
073 */
074@StatelessCheck
075public class IllegalTokenTextCheck
076    extends AbstractCheck {
077
078    /**
079     * A key is pointing to the warning message text in "messages.properties"
080     * file.
081     */
082    public static final String MSG_KEY = "illegal.token.text";
083
084    /**
085     * Define the message which is used to notify about violations;
086     * if empty then the default message is used.
087     */
088    private String message = "";
089
090    /** The format string of the regexp. */
091    private String formatString = "^$";
092
093    /** Define the RegExp for illegal pattern. */
094    private Pattern format = Pattern.compile(formatString);
095
096    /** Control whether to ignore case when matching. */
097    private boolean ignoreCase;
098
099    @Override
100    public int[] getDefaultTokens() {
101        return CommonUtil.EMPTY_INT_ARRAY;
102    }
103
104    @Override
105    public int[] getAcceptableTokens() {
106        return new int[] {
107            TokenTypes.NUM_DOUBLE,
108            TokenTypes.NUM_FLOAT,
109            TokenTypes.NUM_INT,
110            TokenTypes.NUM_LONG,
111            TokenTypes.IDENT,
112            TokenTypes.COMMENT_CONTENT,
113            TokenTypes.STRING_LITERAL,
114            TokenTypes.CHAR_LITERAL,
115            TokenTypes.TEXT_BLOCK_CONTENT,
116        };
117    }
118
119    @Override
120    public int[] getRequiredTokens() {
121        return CommonUtil.EMPTY_INT_ARRAY;
122    }
123
124    @Override
125    public boolean isCommentNodesRequired() {
126        return true;
127    }
128
129    @Override
130    public void visitToken(DetailAST ast) {
131        final String text = ast.getText();
132        if (format.matcher(text).find()) {
133            String customMessage = message;
134            if (customMessage.isEmpty()) {
135                customMessage = MSG_KEY;
136            }
137            log(
138                ast,
139                customMessage,
140                formatString);
141        }
142    }
143
144    /**
145     * Setter to define the message which is used to notify about violations;
146     * if empty then the default message is used.
147     *
148     * @param message custom message which should be used
149     *                 to report about violations.
150     * @since 3.2
151     */
152    public void setMessage(String message) {
153        this.message = Objects.requireNonNullElse(message, "");
154    }
155
156    /**
157     * Setter to define the RegExp for illegal pattern.
158     *
159     * @param format a {@code String} value
160     * @since 3.2
161     */
162    public void setFormat(String format) {
163        formatString = format;
164        updateRegexp();
165    }
166
167    /**
168     * Setter to control whether to ignore case when matching.
169     *
170     * @param caseInsensitive true if the match is case-insensitive.
171     * @since 3.2
172     */
173    public void setIgnoreCase(boolean caseInsensitive) {
174        ignoreCase = caseInsensitive;
175        updateRegexp();
176    }
177
178    /**
179     * Updates the {@link #format} based on the values from {@link #formatString} and
180     * {@link #ignoreCase}.
181     */
182    private void updateRegexp() {
183        final int compileFlags;
184        if (ignoreCase) {
185            compileFlags = Pattern.CASE_INSENSITIVE;
186        }
187        else {
188            compileFlags = 0;
189        }
190        format = CommonUtil.createPattern(formatString, compileFlags);
191    }
192
193}