View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 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.CommonUtils;
29  
30  /**
31   * <p>
32   * Checks for illegal token text.
33   * </p>
34   * <p> An example of how to configure the check to forbid String literals
35   * containing {@code "a href"} is:
36   * </p>
37   * <pre>
38   * &lt;module name="IllegalTokenText"&gt;
39   *     &lt;property name="tokens" value="STRING_LITERAL"/&gt;
40   *     &lt;property name="format" value="a href"/&gt;
41   * &lt;/module&gt;
42   * </pre>
43   * <p> An example of how to configure the check to forbid leading zeros in an
44   * integer literal, other than zero and a hex literal is:
45   * </p>
46   * <pre>
47   * &lt;module name="IllegalTokenText"&gt;
48   *     &lt;property name="tokens" value="NUM_INT,NUM_LONG"/&gt;
49   *     &lt;property name="format" value="^0[^lx]"/&gt;
50   *     &lt;property name="ignoreCase" value="true"/&gt;
51   * &lt;/module&gt;
52   * </pre>
53   * @author Rick Giles
54   */
55  @StatelessCheck
56  public class IllegalTokenTextCheck
57      extends AbstractCheck {
58  
59      /**
60       * A key is pointing to the warning message text in "messages.properties"
61       * file.
62       */
63      public static final String MSG_KEY = "illegal.token.text";
64  
65      /**
66       * Custom message for report if illegal regexp found
67       * ignored if empty.
68       */
69      private String message = "";
70  
71      /** The format string of the regexp. */
72      private String format = "$^";
73  
74      /** The regexp to match against. */
75      private Pattern regexp = Pattern.compile(format);
76  
77      /** The flags to use with the regexp. */
78      private int compileFlags;
79  
80      @Override
81      public int[] getDefaultTokens() {
82          return CommonUtils.EMPTY_INT_ARRAY;
83      }
84  
85      @Override
86      public int[] getAcceptableTokens() {
87          return new int[] {
88              TokenTypes.NUM_DOUBLE,
89              TokenTypes.NUM_FLOAT,
90              TokenTypes.NUM_INT,
91              TokenTypes.NUM_LONG,
92              TokenTypes.IDENT,
93              TokenTypes.COMMENT_CONTENT,
94              TokenTypes.STRING_LITERAL,
95              TokenTypes.CHAR_LITERAL,
96          };
97      }
98  
99      @Override
100     public int[] getRequiredTokens() {
101         return CommonUtils.EMPTY_INT_ARRAY;
102     }
103 
104     @Override
105     public boolean isCommentNodesRequired() {
106         return true;
107     }
108 
109     @Override
110     public void visitToken(DetailAST ast) {
111         final String text = ast.getText();
112         if (regexp.matcher(text).find()) {
113             String customMessage = message;
114             if (customMessage.isEmpty()) {
115                 customMessage = MSG_KEY;
116             }
117             log(
118                 ast.getLineNo(),
119                 ast.getColumnNo(),
120                 customMessage,
121                 format);
122         }
123     }
124 
125     /**
126      * Setter for message property.
127      * @param message custom message which should be used
128      *                 to report about violations.
129      */
130     public void setMessage(String message) {
131         if (message == null) {
132             this.message = "";
133         }
134         else {
135             this.message = message;
136         }
137     }
138 
139     /**
140      * Set the format to the specified regular expression.
141      * @param format a {@code String} value
142      * @throws org.apache.commons.beanutils.ConversionException unable to parse format
143      */
144     public void setFormat(String format) {
145         this.format = format;
146         updateRegexp();
147     }
148 
149     /**
150      * Set whether or not the match is case sensitive.
151      * @param caseInsensitive true if the match is case insensitive.
152      * @noinspection BooleanParameter
153      */
154     public void setIgnoreCase(boolean caseInsensitive) {
155         if (caseInsensitive) {
156             compileFlags = Pattern.CASE_INSENSITIVE;
157         }
158         else {
159             compileFlags = 0;
160         }
161 
162         updateRegexp();
163     }
164 
165     /**
166      * Updates the {@link #regexp} based on the values from {@link #format} and
167      * {@link #compileFlags}.
168      */
169     private void updateRegexp() {
170         regexp = CommonUtils.createPattern(format, compileFlags);
171     }
172 }