View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import java.util.Objects;
23  import java.util.regex.Pattern;
24  
25  import com.puppycrawl.tools.checkstyle.StatelessCheck;
26  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
27  import com.puppycrawl.tools.checkstyle.api.DetailAST;
28  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
29  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
30  
31  /**
32   * <p>
33   * Checks specified tokens text for matching an illegal pattern.
34   * By default, no tokens are specified.
35   * </p>
36   * <ul>
37   * <li>
38   * Property {@code format} - Define the RegExp for illegal pattern.
39   * Type is {@code java.util.regex.Pattern}.
40   * Default value is {@code "^$"}.
41   * </li>
42   * <li>
43   * Property {@code ignoreCase} - Control whether to ignore case when matching.
44   * Type is {@code boolean}.
45   * Default value is {@code false}.
46   * </li>
47   * <li>
48   * Property {@code message} - Define the message which is used to notify about violations;
49   * if empty then the default message is used.
50   * Type is {@code java.lang.String}.
51   * Default value is {@code ""}.
52   * </li>
53   * <li>
54   * Property {@code tokens} - tokens to check
55   * Type is {@code java.lang.String[]}.
56   * Validation type is {@code tokenSet}.
57   * Default value is: {@code ""}.
58   * </li>
59   * </ul>
60   * <p>
61   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
62   * </p>
63   * <p>
64   * Violation Message Keys:
65   * </p>
66   * <ul>
67   * <li>
68   * {@code illegal.token.text}
69   * </li>
70   * </ul>
71   *
72   * @since 3.2
73   */
74  @StatelessCheck
75  public class IllegalTokenTextCheck
76      extends AbstractCheck {
77  
78      /**
79       * A key is pointing to the warning message text in "messages.properties"
80       * file.
81       */
82      public static final String MSG_KEY = "illegal.token.text";
83  
84      /**
85       * Define the message which is used to notify about violations;
86       * if empty then the default message is used.
87       */
88      private String message = "";
89  
90      /** The format string of the regexp. */
91      private String formatString = "^$";
92  
93      /** Define the RegExp for illegal pattern. */
94      private Pattern format = Pattern.compile(formatString);
95  
96      /** Control whether to ignore case when matching. */
97      private boolean ignoreCase;
98  
99      @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 }