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 * <div>
033 * Checks specified tokens text for matching an illegal pattern.
034 * By default, no tokens are specified.
035 * </div>
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 *
061 * <p>
062 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
063 * </p>
064 *
065 * <p>
066 * Violation Message Keys:
067 * </p>
068 * <ul>
069 * <li>
070 * {@code illegal.token.text}
071 * </li>
072 * </ul>
073 *
074 * @since 3.2
075 */
076@StatelessCheck
077public class IllegalTokenTextCheck
078    extends AbstractCheck {
079
080    /**
081     * A key is pointing to the warning message text in "messages.properties"
082     * file.
083     */
084    public static final String MSG_KEY = "illegal.token.text";
085
086    /**
087     * Define the message which is used to notify about violations;
088     * if empty then the default message is used.
089     */
090    private String message = "";
091
092    /** The format string of the regexp. */
093    private String formatString = "^$";
094
095    /** Define the RegExp for illegal pattern. */
096    private Pattern format = Pattern.compile(formatString);
097
098    /** Control whether to ignore case when matching. */
099    private boolean ignoreCase;
100
101    @Override
102    public int[] getDefaultTokens() {
103        return CommonUtil.EMPTY_INT_ARRAY;
104    }
105
106    @Override
107    public int[] getAcceptableTokens() {
108        return new int[] {
109            TokenTypes.NUM_DOUBLE,
110            TokenTypes.NUM_FLOAT,
111            TokenTypes.NUM_INT,
112            TokenTypes.NUM_LONG,
113            TokenTypes.IDENT,
114            TokenTypes.COMMENT_CONTENT,
115            TokenTypes.STRING_LITERAL,
116            TokenTypes.CHAR_LITERAL,
117            TokenTypes.TEXT_BLOCK_CONTENT,
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     *
150     * @param message custom message which should be used
151     *                 to report about violations.
152     * @since 3.2
153     */
154    public void setMessage(String message) {
155        this.message = Objects.requireNonNullElse(message, "");
156    }
157
158    /**
159     * Setter to define the RegExp for illegal pattern.
160     *
161     * @param format a {@code String} value
162     * @since 3.2
163     */
164    public void setFormat(String format) {
165        formatString = format;
166        updateRegexp();
167    }
168
169    /**
170     * Setter to control whether to ignore case when matching.
171     *
172     * @param caseInsensitive true if the match is case-insensitive.
173     * @since 3.2
174     */
175    public void setIgnoreCase(boolean caseInsensitive) {
176        ignoreCase = caseInsensitive;
177        updateRegexp();
178    }
179
180    /**
181     * Updates the {@link #format} based on the values from {@link #formatString} and
182     * {@link #ignoreCase}.
183     */
184    private void updateRegexp() {
185        final int compileFlags;
186        if (ignoreCase) {
187            compileFlags = Pattern.CASE_INSENSITIVE;
188        }
189        else {
190            compileFlags = 0;
191        }
192        format = CommonUtil.createPattern(formatString, compileFlags);
193    }
194
195}