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.javadoc;
21  
22  import java.util.regex.Matcher;
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.FileContents;
29  import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
30  import com.puppycrawl.tools.checkstyle.api.TextBlock;
31  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
32  import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
33  
34  /**
35   * <p>
36   * Outputs a JavaDoc tag as information. Can be used e.g. with the stylesheets
37   * that sort the report by author name.
38   * To define the format for a tag, set property tagFormat to a
39   * regular expression.
40   * This check uses two different severity levels. The normal one is used for
41   * reporting when the tag is missing. The additional one (tagSeverity) is used
42   * for the level of reporting when the tag exists. The default value for
43   * tagSeverity is info.
44   * </p>
45   * <p> An example of how to configure the check for printing author name is:
46   *</p>
47   * <pre>
48   * &lt;module name="WriteTag"&gt;
49   *    &lt;property name="tag" value="@author"/&gt;
50   *    &lt;property name="tagFormat" value="\S"/&gt;
51   * &lt;/module&gt;
52   * </pre>
53   * <p> An example of how to configure the check to print warnings if an
54   * "@incomplete" tag is found, and not print anything if it is not found:
55   *</p>
56   * <pre>
57   * &lt;module name="WriteTag"&gt;
58   *    &lt;property name="tag" value="@incomplete"/&gt;
59   *    &lt;property name="tagFormat" value="\S"/&gt;
60   *    &lt;property name="severity" value="ignore"/&gt;
61   *    &lt;property name="tagSeverity" value="warning"/&gt;
62   * &lt;/module&gt;
63   * </pre>
64   *
65   * @author Daniel Grenner
66   */
67  @StatelessCheck
68  public class WriteTagCheck
69      extends AbstractCheck {
70  
71      /**
72       * A key is pointing to the warning message text in "messages.properties"
73       * file.
74       */
75      public static final String MSG_MISSING_TAG = "type.missingTag";
76  
77      /**
78       * A key is pointing to the warning message text in "messages.properties"
79       * file.
80       */
81      public static final String MSG_WRITE_TAG = "javadoc.writeTag";
82  
83      /**
84       * A key is pointing to the warning message text in "messages.properties"
85       * file.
86       */
87      public static final String MSG_TAG_FORMAT = "type.tagFormat";
88  
89      /** Compiled regexp to match tag. **/
90      private Pattern tagRegExp;
91      /** Compiled regexp to match tag content. **/
92      private Pattern tagFormat;
93  
94      /** Regexp to match tag. */
95      private String tag;
96      /** The severity level of found tag reports. */
97      private SeverityLevel tagSeverity = SeverityLevel.INFO;
98  
99      /**
100      * Sets the tag to check.
101      * @param tag tag to check
102      */
103     public void setTag(String tag) {
104         this.tag = tag;
105         tagRegExp = CommonUtils.createPattern(tag + "\\s*(.*$)");
106     }
107 
108     /**
109      * Set the tag format.
110      * @param pattern a {@code String} value
111      */
112     public void setTagFormat(Pattern pattern) {
113         tagFormat = pattern;
114     }
115 
116     /**
117      * Sets the tag severity level.
118      *
119      * @param severity  The new severity level
120      * @see SeverityLevel
121      */
122     public final void setTagSeverity(SeverityLevel severity) {
123         tagSeverity = severity;
124     }
125 
126     @Override
127     public int[] getDefaultTokens() {
128         return new int[] {TokenTypes.INTERFACE_DEF,
129                           TokenTypes.CLASS_DEF,
130                           TokenTypes.ENUM_DEF,
131                           TokenTypes.ANNOTATION_DEF,
132         };
133     }
134 
135     @Override
136     public int[] getAcceptableTokens() {
137         return new int[] {TokenTypes.INTERFACE_DEF,
138                           TokenTypes.CLASS_DEF,
139                           TokenTypes.ENUM_DEF,
140                           TokenTypes.ANNOTATION_DEF,
141                           TokenTypes.METHOD_DEF,
142                           TokenTypes.CTOR_DEF,
143                           TokenTypes.ENUM_CONSTANT_DEF,
144                           TokenTypes.ANNOTATION_FIELD_DEF,
145         };
146     }
147 
148     @Override
149     public int[] getRequiredTokens() {
150         return CommonUtils.EMPTY_INT_ARRAY;
151     }
152 
153     @Override
154     public void visitToken(DetailAST ast) {
155         final FileContents contents = getFileContents();
156         final int lineNo = ast.getLineNo();
157         final TextBlock cmt =
158             contents.getJavadocBefore(lineNo);
159         if (cmt == null) {
160             log(lineNo, MSG_MISSING_TAG, tag);
161         }
162         else {
163             checkTag(lineNo, cmt.getText());
164         }
165     }
166 
167     /**
168      * Verifies that a type definition has a required tag.
169      * @param lineNo the line number for the type definition.
170      * @param comment the Javadoc comment for the type definition.
171      */
172     private void checkTag(int lineNo, String... comment) {
173         if (tagRegExp != null) {
174             int tagCount = 0;
175             for (int i = 0; i < comment.length; i++) {
176                 final String commentValue = comment[i];
177                 final Matcher matcher = tagRegExp.matcher(commentValue);
178                 if (matcher.find()) {
179                     tagCount += 1;
180                     final int contentStart = matcher.start(1);
181                     final String content = commentValue.substring(contentStart);
182                     if (tagFormat == null || tagFormat.matcher(content).find()) {
183                         logTag(lineNo + i - comment.length, tag, content);
184                     }
185                     else {
186                         log(lineNo + i - comment.length, MSG_TAG_FORMAT, tag, tagFormat.pattern());
187                     }
188                 }
189             }
190             if (tagCount == 0) {
191                 log(lineNo, MSG_MISSING_TAG, tag);
192             }
193         }
194     }
195 
196     /**
197      * Log a message.
198      *
199      * @param line the line number where the error was found
200      * @param tagName the javadoc tag to be logged
201      * @param tagValue the contents of the tag
202      *
203      * @see java.text.MessageFormat
204      */
205     private void logTag(int line, String tagName, String tagValue) {
206         final String originalSeverity = getSeverity();
207         setSeverity(tagSeverity.getName());
208 
209         log(line, MSG_WRITE_TAG, tagName, tagValue);
210 
211         setSeverity(originalSeverity);
212     }
213 }