1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2025 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 com.puppycrawl.tools.checkstyle.StatelessCheck;
23 import com.puppycrawl.tools.checkstyle.api.DetailNode;
24 import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
25 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
26
27 /**
28 * <div>
29 * Checks if the javadoc has
30 * <a href="https://docs.oracle.com/en/java/javase/14/docs/specs/javadoc/doc-comment-spec.html#leading-asterisks">
31 * leading asterisks</a> on each line.
32 * </div>
33 *
34 * <p>
35 * The check does not require asterisks on the first line, nor on the last line if it is blank.
36 * All other lines in a Javadoc should start with {@code *}, including blank lines and code blocks.
37 * </p>
38 *
39 * @since 8.38
40 */
41 @StatelessCheck
42 public class JavadocMissingLeadingAsteriskCheck extends AbstractJavadocCheck {
43
44 /**
45 * A key is pointing to the warning message text in "messages.properties"
46 * file.
47 */
48 public static final String MSG_MISSING_ASTERISK = "javadoc.missing.asterisk";
49
50 @Override
51 public int[] getRequiredJavadocTokens() {
52 return new int[] {
53 JavadocCommentsTokenTypes.NEWLINE,
54 };
55 }
56
57 @Override
58 public int[] getAcceptableJavadocTokens() {
59 return getRequiredJavadocTokens();
60 }
61
62 @Override
63 public int[] getDefaultJavadocTokens() {
64 return getRequiredJavadocTokens();
65 }
66
67 @Override
68 public void visitJavadocToken(DetailNode detailNode) {
69 if (!isInsideHtmlComment(detailNode)) {
70 final DetailNode nextSibling = detailNode.getNextSibling();
71
72 if (nextSibling != null && !isLeadingAsterisk(nextSibling)
73 && !isLastLine(nextSibling)) {
74 log(nextSibling.getLineNumber(), MSG_MISSING_ASTERISK);
75 }
76 }
77 }
78
79 /**
80 * Checks whether the given node is inside an HTML comment.
81 *
82 * @param detailNode the node to process
83 * @return {@code true} if the node is inside an HTML comment
84 */
85 private static boolean isInsideHtmlComment(DetailNode detailNode) {
86 final int parentType = detailNode.getParent().getType();
87 return parentType == JavadocCommentsTokenTypes.HTML_COMMENT_CONTENT
88 || parentType == JavadocCommentsTokenTypes.HTML_COMMENT;
89
90 }
91
92 /**
93 * Checks whether the given node is a leading asterisk.
94 *
95 * @param detailNode the node to process
96 * @return {@code true} if the node is {@link JavadocCommentsTokenTypes#LEADING_ASTERISK}
97 */
98 private static boolean isLeadingAsterisk(DetailNode detailNode) {
99 return detailNode.getType() == JavadocCommentsTokenTypes.LEADING_ASTERISK;
100 }
101
102 /**
103 * Checks whether this node is the end of a Javadoc comment,
104 * optionally preceded by blank text.
105 *
106 * @param detailNode the node to process
107 * @return {@code true} if the node is {@code null}
108 */
109 private static boolean isLastLine(DetailNode detailNode) {
110 final DetailNode node;
111 if (CommonUtil.isBlank(detailNode.getText())) {
112 node = detailNode.getNextSibling();
113 }
114 else {
115 node = detailNode;
116 }
117 return node == null;
118 }
119
120 }