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.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.CommonUtil; 33 34 /** 35 * <div> 36 * Requires user defined Javadoc tag to be present in Javadoc comment with defined format. 37 * To define the format for a tag, set property tagFormat to a regular expression. 38 * Property tagSeverity is used for severity of events when the tag exists. 39 * No violation reported in case there is no javadoc. 40 * </div> 41 * 42 * <ul> 43 * <li> 44 * Property {@code tag} - Specify the name of tag. 45 * Type is {@code java.lang.String}. 46 * Default value is {@code null}. 47 * </li> 48 * <li> 49 * Property {@code tagFormat} - Specify the regexp to match tag content. 50 * Type is {@code java.util.regex.Pattern}. 51 * Default value is {@code null}. 52 * </li> 53 * <li> 54 * Property {@code tagSeverity} - Specify the severity level when tag is found and printed. 55 * Type is {@code com.puppycrawl.tools.checkstyle.api.SeverityLevel}. 56 * Default value is {@code info}. 57 * </li> 58 * <li> 59 * Property {@code tokens} - tokens to check 60 * Type is {@code java.lang.String[]}. 61 * Validation type is {@code tokenSet}. 62 * Default value is: 63 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF"> 64 * INTERFACE_DEF</a>, 65 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF"> 66 * CLASS_DEF</a>, 67 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF"> 68 * ENUM_DEF</a>, 69 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_DEF"> 70 * ANNOTATION_DEF</a>, 71 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF"> 72 * RECORD_DEF</a>. 73 * </li> 74 * </ul> 75 * 76 * <p> 77 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 78 * </p> 79 * 80 * <p> 81 * Violation Message Keys: 82 * </p> 83 * <ul> 84 * <li> 85 * {@code javadoc.writeTag} 86 * </li> 87 * <li> 88 * {@code type.missingTag} 89 * </li> 90 * <li> 91 * {@code type.tagFormat} 92 * </li> 93 * </ul> 94 * 95 * @since 4.2 96 */ 97 @StatelessCheck 98 public class WriteTagCheck 99 extends AbstractCheck { 100 101 /** 102 * A key is pointing to the warning message text in "messages.properties" 103 * file. 104 */ 105 public static final String MSG_MISSING_TAG = "type.missingTag"; 106 107 /** 108 * A key is pointing to the warning message text in "messages.properties" 109 * file. 110 */ 111 public static final String MSG_WRITE_TAG = "javadoc.writeTag"; 112 113 /** 114 * A key is pointing to the warning message text in "messages.properties" 115 * file. 116 */ 117 public static final String MSG_TAG_FORMAT = "type.tagFormat"; 118 119 /** Compiled regexp to match tag. */ 120 private Pattern tagRegExp; 121 /** Specify the regexp to match tag content. */ 122 private Pattern tagFormat; 123 124 /** Specify the name of tag. */ 125 private String tag; 126 /** Specify the severity level when tag is found and printed. */ 127 private SeverityLevel tagSeverity = SeverityLevel.INFO; 128 129 /** 130 * Setter to specify the name of tag. 131 * 132 * @param tag tag to check 133 * @since 4.2 134 */ 135 public void setTag(String tag) { 136 this.tag = tag; 137 tagRegExp = CommonUtil.createPattern(tag + "\\s*(.*$)"); 138 } 139 140 /** 141 * Setter to specify the regexp to match tag content. 142 * 143 * @param pattern a {@code String} value 144 * @since 4.2 145 */ 146 public void setTagFormat(Pattern pattern) { 147 tagFormat = pattern; 148 } 149 150 /** 151 * Setter to specify the severity level when tag is found and printed. 152 * 153 * @param severity The new severity level 154 * @see SeverityLevel 155 * @since 4.2 156 */ 157 public final void setTagSeverity(SeverityLevel severity) { 158 tagSeverity = severity; 159 } 160 161 @Override 162 public int[] getDefaultTokens() { 163 return new int[] { 164 TokenTypes.INTERFACE_DEF, 165 TokenTypes.CLASS_DEF, 166 TokenTypes.ENUM_DEF, 167 TokenTypes.ANNOTATION_DEF, 168 TokenTypes.RECORD_DEF, 169 }; 170 } 171 172 @Override 173 public int[] getAcceptableTokens() { 174 return new int[] { 175 TokenTypes.INTERFACE_DEF, 176 TokenTypes.CLASS_DEF, 177 TokenTypes.ENUM_DEF, 178 TokenTypes.ANNOTATION_DEF, 179 TokenTypes.METHOD_DEF, 180 TokenTypes.CTOR_DEF, 181 TokenTypes.ENUM_CONSTANT_DEF, 182 TokenTypes.ANNOTATION_FIELD_DEF, 183 TokenTypes.RECORD_DEF, 184 TokenTypes.COMPACT_CTOR_DEF, 185 }; 186 } 187 188 @Override 189 public int[] getRequiredTokens() { 190 return CommonUtil.EMPTY_INT_ARRAY; 191 } 192 193 // suppress deprecation until https://github.com/checkstyle/checkstyle/issues/11166 194 @SuppressWarnings("deprecation") 195 @Override 196 public void visitToken(DetailAST ast) { 197 final FileContents contents = getFileContents(); 198 final int lineNo = ast.getLineNo(); 199 final TextBlock cmt = 200 contents.getJavadocBefore(lineNo); 201 if (cmt != null) { 202 checkTag(lineNo, cmt.getText()); 203 } 204 } 205 206 /** 207 * Verifies that a type definition has a required tag. 208 * 209 * @param lineNo the line number for the type definition. 210 * @param comment the Javadoc comment for the type definition. 211 */ 212 private void checkTag(int lineNo, String... comment) { 213 if (tagRegExp != null) { 214 boolean hasTag = false; 215 for (int i = 0; i < comment.length; i++) { 216 final String commentValue = comment[i]; 217 final Matcher matcher = tagRegExp.matcher(commentValue); 218 if (matcher.find()) { 219 hasTag = true; 220 final int contentStart = matcher.start(1); 221 final String content = commentValue.substring(contentStart); 222 if (tagFormat == null || tagFormat.matcher(content).find()) { 223 logTag(lineNo + i - comment.length, tag, content); 224 } 225 else { 226 log(lineNo + i - comment.length, MSG_TAG_FORMAT, tag, tagFormat.pattern()); 227 } 228 } 229 } 230 if (!hasTag) { 231 log(lineNo, MSG_MISSING_TAG, tag); 232 } 233 } 234 } 235 236 /** 237 * Log a message. 238 * 239 * @param line the line number where the violation was found 240 * @param tagName the javadoc tag to be logged 241 * @param tagValue the contents of the tag 242 * 243 * @see java.text.MessageFormat 244 */ 245 private void logTag(int line, String tagName, String tagValue) { 246 final String originalSeverity = getSeverity(); 247 setSeverity(tagSeverity.getName()); 248 249 log(line, MSG_WRITE_TAG, tagName, tagValue); 250 251 setSeverity(originalSeverity); 252 } 253 254 }