001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2025 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.Arrays; 023import java.util.Set; 024import java.util.regex.Matcher; 025import java.util.regex.Pattern; 026import java.util.stream.Collectors; 027 028import com.puppycrawl.tools.checkstyle.StatelessCheck; 029import com.puppycrawl.tools.checkstyle.api.DetailNode; 030import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes; 031 032/** 033 * <div> 034 * Checks that a 035 * <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/javadoc/doc-comment-spec.html#block-tags"> 036 * javadoc block tag</a> appears only at the beginning of a line, ignoring 037 * leading asterisks and white space. A block tag is a token that starts with 038 * {@code @} symbol and is preceded by a whitespace. This check ignores block 039 * tags in comments and inside inline tags {@code } and {@literal }. 040 * </div> 041 * 042 * <p> 043 * Rationale: according to 044 * <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/javadoc/doc-comment-spec.html#block-tags"> 045 * the specification</a> all javadoc block tags should be placed at the beginning 046 * of a line. Tags that are not placed at the beginning are treated as plain text. 047 * To recognize intentional tag placement to text area it is better to escape the 048 * {@code @} symbol, and all non-escaped tags should be located at the beginning 049 * of the line. See NOTE section for details on how to escape. 050 * </p> 051 * 052 * <p> 053 * Notes: 054 * To place a tag explicitly as text, escape the {@code @} symbol with HTML entity 055 * &#64; or place it inside {@code {@code }}, for example: 056 * </p> 057 * <div class="wrapper"><pre class="prettyprint"><code class="language-java"> 058 * /** 059 * * &#64;serial literal in {@code @serial} Javadoc tag. 060 * */ 061 * </code></pre></div> 062 * 063 * @since 8.24 064 */ 065@StatelessCheck 066public class JavadocBlockTagLocationCheck extends AbstractJavadocCheck { 067 068 /** 069 * A key is pointing to the warning message text in "messages.properties" file. 070 */ 071 public static final String MSG_BLOCK_TAG_LOCATION = "javadoc.blockTagLocation"; 072 073 /** 074 * This regexp is used to extract the javadoc tags. 075 */ 076 private static final Pattern JAVADOC_BLOCK_TAG_PATTERN = Pattern.compile("\\s@(\\w+)"); 077 078 /** 079 * Block tags from Java 11 080 * <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/javadoc/doc-comment-spec.html"> 081 * Documentation Comment Specification</a>. 082 */ 083 private static final String[] DEFAULT_TAGS = { 084 "author", 085 "deprecated", 086 "exception", 087 "hidden", 088 "param", 089 "provides", 090 "return", 091 "see", 092 "serial", 093 "serialData", 094 "serialField", 095 "since", 096 "throws", 097 "uses", 098 "version", 099 }; 100 101 /** 102 * Specify the javadoc tags to process. 103 */ 104 private Set<String> tags; 105 106 /** 107 * Creates a new {@code JavadocBlockTagLocationCheck} instance with default settings. 108 */ 109 public JavadocBlockTagLocationCheck() { 110 setTags(DEFAULT_TAGS); 111 } 112 113 /** 114 * Setter to specify the javadoc tags to process. 115 * 116 * @param values user's values. 117 * @since 8.24 118 */ 119 public final void setTags(String... values) { 120 tags = Arrays.stream(values).collect(Collectors.toUnmodifiableSet()); 121 } 122 123 /** 124 * The javadoc tokens that this check must be registered for. According to 125 * <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/javadoc/doc-comment-spec.html#block-tags"> 126 * the specs</a> each block tag must appear at the beginning of a line, otherwise 127 * it will be interpreted as a plain text. This check looks for a block tag 128 * in the javadoc text, thus it needs the {@code TEXT} tokens. 129 * 130 * @return the javadoc token set this must be registered for. 131 * @see JavadocTokenTypes 132 */ 133 @Override 134 public int[] getRequiredJavadocTokens() { 135 return new int[] { 136 JavadocTokenTypes.TEXT, 137 }; 138 } 139 140 @Override 141 public int[] getAcceptableJavadocTokens() { 142 return getRequiredJavadocTokens(); 143 } 144 145 @Override 146 public int[] getDefaultJavadocTokens() { 147 return getRequiredJavadocTokens(); 148 } 149 150 @Override 151 public void visitJavadocToken(DetailNode ast) { 152 if (!isCommentOrInlineTag(ast.getParent())) { 153 final Matcher tagMatcher = JAVADOC_BLOCK_TAG_PATTERN.matcher(ast.getText()); 154 while (tagMatcher.find()) { 155 final String tagName = tagMatcher.group(1); 156 if (tags.contains(tagName)) { 157 log(ast.getLineNumber(), MSG_BLOCK_TAG_LOCATION, tagName); 158 } 159 } 160 } 161 } 162 163 /** 164 * Checks if the node can contain an unescaped block tag without violation. 165 * 166 * @param node to check 167 * @return {@code true} if node is {@code @code}, {@code @literal} or HTML comment. 168 */ 169 private static boolean isCommentOrInlineTag(DetailNode node) { 170 return node.getType() == JavadocTokenTypes.JAVADOC_INLINE_TAG 171 || node.getType() == JavadocTokenTypes.HTML_COMMENT; 172 } 173 174}