001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.javadoc; 021 022import java.util.regex.Matcher; 023import java.util.regex.Pattern; 024 025import com.puppycrawl.tools.checkstyle.StatelessCheck; 026import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 027import com.puppycrawl.tools.checkstyle.api.DetailAST; 028import com.puppycrawl.tools.checkstyle.api.FileContents; 029import com.puppycrawl.tools.checkstyle.api.SeverityLevel; 030import com.puppycrawl.tools.checkstyle.api.TextBlock; 031import com.puppycrawl.tools.checkstyle.api.TokenTypes; 032import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 033 034/** 035 * <p> 036 * Requires user defined Javadoc tag to be present in Javadoc comment with defined format. 037 * To define the format for a tag, set property tagFormat to a regular expression. 038 * Property tagSeverity is used for severity of events when the tag exists. 039 * </p> 040 * <ul> 041 * <li> 042 * Property {@code tag} - Specify the name of tag. 043 * Type is {@code java.lang.String}. 044 * Default value is {@code null}. 045 * </li> 046 * <li> 047 * Property {@code tagFormat} - Specify the regexp to match tag content. 048 * Type is {@code java.util.regex.Pattern}. 049 * Default value is {@code null}. 050 * </li> 051 * <li> 052 * Property {@code tagSeverity} - Specify the severity level when tag is found and printed. 053 * Type is {@code com.puppycrawl.tools.checkstyle.api.SeverityLevel}. 054 * Default value is {@code info}. 055 * </li> 056 * <li> 057 * Property {@code tokens} - tokens to check 058 * Type is {@code java.lang.String[]}. 059 * Validation type is {@code tokenSet}. 060 * Default value is: 061 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF"> 062 * INTERFACE_DEF</a>, 063 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF"> 064 * CLASS_DEF</a>, 065 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF"> 066 * ENUM_DEF</a>, 067 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_DEF"> 068 * ANNOTATION_DEF</a>, 069 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF"> 070 * RECORD_DEF</a>. 071 * </li> 072 * </ul> 073 * <p> 074 * Example of default Check configuration that do nothing. 075 * </p> 076 * <pre> 077 * <module name="WriteTag"/> 078 * </pre> 079 * <p> 080 * Example: 081 * </p> 082 * <pre> 083 * /** 084 * * Some class 085 * */ 086 * public class Test { 087 * /** some doc */ 088 * void foo() {} 089 * } 090 * </pre> 091 * <p> 092 * To configure Check to demand some special tag (for example {@code @since}) 093 * to be present on classes javadoc. 094 * </p> 095 * <pre> 096 * <module name="WriteTag"> 097 * <property name="tag" value="@since"/> 098 * </module> 099 * </pre> 100 * <p> 101 * Example: 102 * </p> 103 * <pre> 104 * /** 105 * * Some class 106 * */ 107 * public class Test { // violation as required tag is missed 108 * /** some doc */ 109 * void foo() {} // OK, as methods are not checked by default 110 * } 111 * </pre> 112 * <p> 113 * To configure Check to demand some special tag (for example {@code @since}) 114 * to be present on method javadocs also in addition to default tokens. 115 * </p> 116 * <pre> 117 * <module name="WriteTag"> 118 * <property name="tag" value="@since"/> 119 * <property name="tokens" 120 * value="INTERFACE_DEF, CLASS_DEF, ENUM_DEF, ANNOTATION_DEF, RECORD_DEF, METHOD_DEF" /> 121 * </module> 122 * </pre> 123 * <p> 124 * Example: 125 * </p> 126 * <pre> 127 * /** 128 * * Some class 129 * */ 130 * public class Test { // violation as required tag is missed 131 * /** some doc */ 132 * void foo() {} // violation as required tag is missed 133 * } 134 * </pre> 135 * <p> 136 * To configure Check to demand {@code @since} tag 137 * to be present with digital value on method javadocs also in addition to default tokens. 138 * Attention: usage of non "ignore" in tagSeverity will print violation with such severity 139 * on each presence of such tag. 140 * </p> 141 * <pre> 142 * <module name="WriteTag"> 143 * <property name="tag" value="@since"/> 144 * <property name="tokens" 145 * value="INTERFACE_DEF, CLASS_DEF, ENUM_DEF, ANNOTATION_DEF, RECORD_DEF, METHOD_DEF" /> 146 * <property name="tagFormat" value="[1-9\.]"/> 147 * <property name="tagSeverity" value="ignore"/> 148 * </module> 149 * </pre> 150 * <p> 151 * Example: 152 * </p> 153 * <pre> 154 * /** 155 * * Some class 156 * * @since 1.2 157 * */ 158 * public class Test { 159 * /** some doc 160 * * @since violation 161 * */ 162 * void foo() {} 163 * } 164 * </pre> 165 * <p> 166 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 167 * </p> 168 * <p> 169 * Violation Message Keys: 170 * </p> 171 * <ul> 172 * <li> 173 * {@code javadoc.writeTag} 174 * </li> 175 * <li> 176 * {@code type.missingTag} 177 * </li> 178 * <li> 179 * {@code type.tagFormat} 180 * </li> 181 * </ul> 182 * 183 * @since 4.2 184 */ 185@StatelessCheck 186public class WriteTagCheck 187 extends AbstractCheck { 188 189 /** 190 * A key is pointing to the warning message text in "messages.properties" 191 * file. 192 */ 193 public static final String MSG_MISSING_TAG = "type.missingTag"; 194 195 /** 196 * A key is pointing to the warning message text in "messages.properties" 197 * file. 198 */ 199 public static final String MSG_WRITE_TAG = "javadoc.writeTag"; 200 201 /** 202 * A key is pointing to the warning message text in "messages.properties" 203 * file. 204 */ 205 public static final String MSG_TAG_FORMAT = "type.tagFormat"; 206 207 /** Compiled regexp to match tag. */ 208 private Pattern tagRegExp; 209 /** Specify the regexp to match tag content. */ 210 private Pattern tagFormat; 211 212 /** Specify the name of tag. */ 213 private String tag; 214 /** Specify the severity level when tag is found and printed. */ 215 private SeverityLevel tagSeverity = SeverityLevel.INFO; 216 217 /** 218 * Setter to specify the name of tag. 219 * 220 * @param tag tag to check 221 */ 222 public void setTag(String tag) { 223 this.tag = tag; 224 tagRegExp = CommonUtil.createPattern(tag + "\\s*(.*$)"); 225 } 226 227 /** 228 * Setter to specify the regexp to match tag content. 229 * 230 * @param pattern a {@code String} value 231 */ 232 public void setTagFormat(Pattern pattern) { 233 tagFormat = pattern; 234 } 235 236 /** 237 * Setter to specify the severity level when tag is found and printed. 238 * 239 * @param severity The new severity level 240 * @see SeverityLevel 241 */ 242 public final void setTagSeverity(SeverityLevel severity) { 243 tagSeverity = severity; 244 } 245 246 @Override 247 public int[] getDefaultTokens() { 248 return new int[] { 249 TokenTypes.INTERFACE_DEF, 250 TokenTypes.CLASS_DEF, 251 TokenTypes.ENUM_DEF, 252 TokenTypes.ANNOTATION_DEF, 253 TokenTypes.RECORD_DEF, 254 }; 255 } 256 257 @Override 258 public int[] getAcceptableTokens() { 259 return new int[] { 260 TokenTypes.INTERFACE_DEF, 261 TokenTypes.CLASS_DEF, 262 TokenTypes.ENUM_DEF, 263 TokenTypes.ANNOTATION_DEF, 264 TokenTypes.METHOD_DEF, 265 TokenTypes.CTOR_DEF, 266 TokenTypes.ENUM_CONSTANT_DEF, 267 TokenTypes.ANNOTATION_FIELD_DEF, 268 TokenTypes.RECORD_DEF, 269 TokenTypes.COMPACT_CTOR_DEF, 270 }; 271 } 272 273 @Override 274 public int[] getRequiredTokens() { 275 return CommonUtil.EMPTY_INT_ARRAY; 276 } 277 278 @Override 279 public void visitToken(DetailAST ast) { 280 final FileContents contents = getFileContents(); 281 final int lineNo = ast.getLineNo(); 282 final TextBlock cmt = 283 contents.getJavadocBefore(lineNo); 284 if (cmt == null) { 285 log(lineNo, MSG_MISSING_TAG, tag); 286 } 287 else { 288 checkTag(lineNo, cmt.getText()); 289 } 290 } 291 292 /** 293 * Verifies that a type definition has a required tag. 294 * 295 * @param lineNo the line number for the type definition. 296 * @param comment the Javadoc comment for the type definition. 297 */ 298 private void checkTag(int lineNo, String... comment) { 299 if (tagRegExp != null) { 300 boolean hasTag = false; 301 for (int i = 0; i < comment.length; i++) { 302 final String commentValue = comment[i]; 303 final Matcher matcher = tagRegExp.matcher(commentValue); 304 if (matcher.find()) { 305 hasTag = true; 306 final int contentStart = matcher.start(1); 307 final String content = commentValue.substring(contentStart); 308 if (tagFormat == null || tagFormat.matcher(content).find()) { 309 logTag(lineNo + i - comment.length, tag, content); 310 } 311 else { 312 log(lineNo + i - comment.length, MSG_TAG_FORMAT, tag, tagFormat.pattern()); 313 } 314 } 315 } 316 if (!hasTag) { 317 log(lineNo, MSG_MISSING_TAG, tag); 318 } 319 } 320 } 321 322 /** 323 * Log a message. 324 * 325 * @param line the line number where the violation was found 326 * @param tagName the javadoc tag to be logged 327 * @param tagValue the contents of the tag 328 * 329 * @see java.text.MessageFormat 330 */ 331 private void logTag(int line, String tagName, String tagValue) { 332 final String originalSeverity = getSeverity(); 333 setSeverity(tagSeverity.getName()); 334 335 log(line, MSG_WRITE_TAG, tagName, tagValue); 336 337 setSeverity(originalSeverity); 338 } 339 340}