1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2026 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 /**
51 * Creates a new {@code JavadocMissingLeadingAsteriskCheck} instance.
52 */
53 public JavadocMissingLeadingAsteriskCheck() {
54 // no code by default
55 }
56
57 @Override
58 public int[] getRequiredJavadocTokens() {
59 return new int[] {
60 JavadocCommentsTokenTypes.NEWLINE,
61 };
62 }
63
64 @Override
65 public int[] getAcceptableJavadocTokens() {
66 return getRequiredJavadocTokens();
67 }
68
69 @Override
70 public int[] getDefaultJavadocTokens() {
71 return getRequiredJavadocTokens();
72 }
73
74 @Override
75 public void visitJavadocToken(DetailNode detailNode) {
76 if (!isInsideHtmlComment(detailNode)) {
77 final DetailNode nextSibling = detailNode.getNextSibling();
78
79 if (nextSibling != null && !isLeadingAsterisk(nextSibling)
80 && !isLastLine(nextSibling)) {
81 log(nextSibling.getLineNumber(), MSG_MISSING_ASTERISK);
82 }
83 }
84 }
85
86 /**
87 * Checks whether the given node is inside an HTML comment.
88 *
89 * @param detailNode the node to process
90 * @return {@code true} if the node is inside an HTML comment
91 */
92 private static boolean isInsideHtmlComment(DetailNode detailNode) {
93 final int parentType = detailNode.getParent().getType();
94 return parentType == JavadocCommentsTokenTypes.HTML_COMMENT_CONTENT
95 || parentType == JavadocCommentsTokenTypes.HTML_COMMENT;
96
97 }
98
99 /**
100 * Checks whether the given node is a leading asterisk.
101 *
102 * @param detailNode the node to process
103 * @return {@code true} if the node is a leading asterisk
104 */
105 private static boolean isLeadingAsterisk(DetailNode detailNode) {
106 return detailNode.getType() == JavadocCommentsTokenTypes.LEADING_ASTERISK
107 || detailNode.getType() == JavadocCommentsTokenTypes.LEADING_ASTERISKS;
108 }
109
110 /**
111 * Checks whether this node is the end of a Javadoc comment,
112 * optionally preceded by blank text.
113 *
114 * @param detailNode the node to process
115 * @return {@code true} if the node is {@code null}
116 */
117 private static boolean isLastLine(DetailNode detailNode) {
118 final DetailNode node;
119 if (CommonUtil.isBlank(detailNode.getText())) {
120 node = detailNode.getNextSibling();
121 }
122 else {
123 node = detailNode;
124 }
125 return node == null;
126 }
127
128 }