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   * <div>
33   * Checks specified tokens text for matching an illegal pattern.
34   * By default, no tokens are specified.
35   * </div>
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   *
61   * <p>
62   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
63   * </p>
64   *
65   * <p>
66   * Violation Message Keys:
67   * </p>
68   * <ul>
69   * <li>
70   * {@code illegal.token.text}
71   * </li>
72   * </ul>
73   *
74   * @since 3.2
75   */
76  @StatelessCheck
77  public class IllegalTokenTextCheck
78      extends AbstractCheck {
79  
80      /**
81       * A key is pointing to the warning message text in "messages.properties"
82       * file.
83       */
84      public static final String MSG_KEY = "illegal.token.text";
85  
86      /**
87       * Define the message which is used to notify about violations;
88       * if empty then the default message is used.
89       */
90      private String message = "";
91  
92      /** The format string of the regexp. */
93      private String formatString = "^$";
94  
95      /** Define the RegExp for illegal pattern. */
96      private Pattern format = Pattern.compile(formatString);
97  
98      /** Control whether to ignore case when matching. */
99      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 }