001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2024 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            TokenTypes.STRING_TEMPLATE_CONTENT,
117        };
118    }
119
120    @Override
121    public int[] getRequiredTokens() {
122        return CommonUtil.EMPTY_INT_ARRAY;
123    }
124
125    @Override
126    public boolean isCommentNodesRequired() {
127        return true;
128    }
129
130    @Override
131    public void visitToken(DetailAST ast) {
132        final String text = ast.getText();
133        if (format.matcher(text).find()) {
134            String customMessage = message;
135            if (customMessage.isEmpty()) {
136                customMessage = MSG_KEY;
137            }
138            log(
139                ast,
140                customMessage,
141                formatString);
142        }
143    }
144
145    /**
146     * Setter to define the message which is used to notify about violations;
147     * if empty then the default message is used.
148     *
149     * @param message custom message which should be used
150     *                 to report about violations.
151     * @since 3.2
152     */
153    public void setMessage(String message) {
154        this.message = Objects.requireNonNullElse(message, "");
155    }
156
157    /**
158     * Setter to define the RegExp for illegal pattern.
159     *
160     * @param format a {@code String} value
161     * @since 3.2
162     */
163    public void setFormat(String format) {
164        formatString = format;
165        updateRegexp();
166    }
167
168    /**
169     * Setter to control whether to ignore case when matching.
170     *
171     * @param caseInsensitive true if the match is case-insensitive.
172     * @since 3.2
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}