001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2025 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 * <div>
033 * Checks specified tokens text for matching an illegal pattern.
034 * By default, no tokens are specified.
035 * </div>
036 *
037 * @since 3.2
038 */
039@StatelessCheck
040public class IllegalTokenTextCheck
041    extends AbstractCheck {
042
043    /**
044     * A key is pointing to the warning message text in "messages.properties"
045     * file.
046     */
047    public static final String MSG_KEY = "illegal.token.text";
048
049    /**
050     * Define the message which is used to notify about violations;
051     * if empty then the default message is used.
052     */
053    private String message = "";
054
055    /** The format string of the regexp. */
056    private String formatString = "^$";
057
058    /** Define the RegExp for illegal pattern. */
059    private Pattern format = Pattern.compile(formatString);
060
061    /** Control whether to ignore case when matching. */
062    private boolean ignoreCase;
063
064    @Override
065    public int[] getDefaultTokens() {
066        return CommonUtil.EMPTY_INT_ARRAY;
067    }
068
069    @Override
070    public int[] getAcceptableTokens() {
071        return new int[] {
072            TokenTypes.NUM_DOUBLE,
073            TokenTypes.NUM_FLOAT,
074            TokenTypes.NUM_INT,
075            TokenTypes.NUM_LONG,
076            TokenTypes.IDENT,
077            TokenTypes.COMMENT_CONTENT,
078            TokenTypes.STRING_LITERAL,
079            TokenTypes.CHAR_LITERAL,
080            TokenTypes.TEXT_BLOCK_CONTENT,
081        };
082    }
083
084    @Override
085    public int[] getRequiredTokens() {
086        return CommonUtil.EMPTY_INT_ARRAY;
087    }
088
089    @Override
090    public boolean isCommentNodesRequired() {
091        return true;
092    }
093
094    @Override
095    public void visitToken(DetailAST ast) {
096        final String text = ast.getText();
097        if (format.matcher(text).find()) {
098            String customMessage = message;
099            if (customMessage.isEmpty()) {
100                customMessage = MSG_KEY;
101            }
102            log(
103                ast,
104                customMessage,
105                formatString);
106        }
107    }
108
109    /**
110     * Setter to define the message which is used to notify about violations;
111     * if empty then the default message is used.
112     *
113     * @param message custom message which should be used
114     *                 to report about violations.
115     * @since 3.2
116     */
117    public void setMessage(String message) {
118        this.message = Objects.requireNonNullElse(message, "");
119    }
120
121    /**
122     * Setter to define the RegExp for illegal pattern.
123     *
124     * @param format a {@code String} value
125     * @since 3.2
126     */
127    public void setFormat(String format) {
128        formatString = format;
129        updateRegexp();
130    }
131
132    /**
133     * Setter to control whether to ignore case when matching.
134     *
135     * @param caseInsensitive true if the match is case-insensitive.
136     * @since 3.2
137     */
138    public void setIgnoreCase(boolean caseInsensitive) {
139        ignoreCase = caseInsensitive;
140        updateRegexp();
141    }
142
143    /**
144     * Updates the {@link #format} based on the values from {@link #formatString} and
145     * {@link #ignoreCase}.
146     */
147    private void updateRegexp() {
148        final int compileFlags;
149        if (ignoreCase) {
150            compileFlags = Pattern.CASE_INSENSITIVE;
151        }
152        else {
153            compileFlags = 0;
154        }
155        format = CommonUtil.createPattern(formatString, compileFlags);
156    }
157
158}