1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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.regex.Pattern;
23  
24  import com.puppycrawl.tools.checkstyle.StatelessCheck;
25  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
26  import com.puppycrawl.tools.checkstyle.api.DetailAST;
27  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
28  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
29  
30  /**
31   * <p>
32   * Checks specified tokens text for matching an illegal pattern from {@code format} property.
33   * By default no tokens are specified.
34   * </p>
35   * <ul>
36   * <li>
37   * Property {@code format} - Define the RegExp for illegal pattern.
38   * Default value is {@code "^$" (empty)}.
39   * </li>
40   * <li>
41   * Property {@code ignoreCase} - Control whether to ignore case when matching.
42   * Default value is {@code false}.
43   * </li>
44   * <li>
45   * Property {@code message} - Define the message which is used to notify about violations;
46   * if empty then the default message is used.
47   * Default value is {@code ""}.
48   * </li>
49   * <li>
50   * Property {@code tokens} - tokens to check
51   * Default value is: empty.
52   * </li>
53   * </ul>
54   * <p>
55   * To configure the check to forbid String literals containing {@code "a href"}:
56   * </p>
57   * <pre>
58   * &lt;module name=&quot;IllegalTokenText&quot;&gt;
59   *   &lt;property name=&quot;tokens&quot; value=&quot;STRING_LITERAL&quot;/&gt;
60   *   &lt;property name=&quot;format&quot; value=&quot;a href&quot;/&gt;
61   * &lt;/module&gt;
62   * </pre>
63   * <p>
64   * To configure the check to forbid leading zeros in an integer literal,
65   * other than zero and a hex literal:
66   * </p>
67   * <pre>
68   * &lt;module name=&quot;IllegalTokenText&quot;&gt;
69   *   &lt;property name=&quot;tokens&quot; value=&quot;NUM_INT,NUM_LONG&quot;/&gt;
70   *   &lt;property name=&quot;format&quot; value=&quot;^0[^lx]&quot;/&gt;
71   *   &lt;property name=&quot;ignoreCase&quot; value=&quot;true&quot;/&gt;
72   * &lt;/module&gt;
73   * </pre>
74   *
75   * @since 3.2
76   */
77  @StatelessCheck
78  public class IllegalTokenTextCheck
79      extends AbstractCheck {
80  
81      /**
82       * A key is pointing to the warning message text in "messages.properties"
83       * file.
84       */
85      public static final String MSG_KEY = "illegal.token.text";
86  
87      /**
88       * Define the message which is used to notify about violations;
89       * if empty then the default message is used.
90       */
91      private String message = "";
92  
93      /** The format string of the regexp. */
94      private String formatString = "^$";
95  
96      /** Define the RegExp for illegal pattern. */
97      private Pattern format = Pattern.compile(formatString);
98  
99      /** Control whether to ignore case when matching. */
100     private boolean ignoreCase;
101 
102     @Override
103     public int[] getDefaultTokens() {
104         return CommonUtil.EMPTY_INT_ARRAY;
105     }
106 
107     @Override
108     public int[] getAcceptableTokens() {
109         return new int[] {
110             TokenTypes.NUM_DOUBLE,
111             TokenTypes.NUM_FLOAT,
112             TokenTypes.NUM_INT,
113             TokenTypes.NUM_LONG,
114             TokenTypes.IDENT,
115             TokenTypes.COMMENT_CONTENT,
116             TokenTypes.STRING_LITERAL,
117             TokenTypes.CHAR_LITERAL,
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      * @param message custom message which should be used
150      *                 to report about violations.
151      */
152     public void setMessage(String message) {
153         if (message == null) {
154             this.message = "";
155         }
156         else {
157             this.message = message;
158         }
159     }
160 
161     /**
162      * Setter to define the RegExp for illegal pattern.
163      * @param format a {@code String} value
164      * @throws org.apache.commons.beanutils.ConversionException unable to parse format
165      */
166     public void setFormat(String format) {
167         formatString = format;
168         updateRegexp();
169     }
170 
171     /**
172      * Setter to control whether to ignore case when matching.
173      * @param caseInsensitive true if the match is case insensitive.
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 }