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 com.puppycrawl.tools.checkstyle.StatelessCheck;
023import com.puppycrawl.tools.checkstyle.api.DetailNode;
024import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
025import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
026
027/**
028 * <div>
029 * Checks if the javadoc has
030 * <a href="https://docs.oracle.com/en/java/javase/14/docs/specs/javadoc/doc-comment-spec.html#leading-asterisks">
031 * leading asterisks</a> on each line.
032 * </div>
033 *
034 * <p>
035 * The check does not require asterisks on the first line, nor on the last line if it is blank.
036 * All other lines in a Javadoc should start with {@code *}, including blank lines and code blocks.
037 * </p>
038 *
039 * @since 8.38
040 */
041@StatelessCheck
042public class JavadocMissingLeadingAsteriskCheck extends AbstractJavadocCheck {
043
044    /**
045     * A key is pointing to the warning message text in "messages.properties"
046     * file.
047     */
048    public static final String MSG_MISSING_ASTERISK = "javadoc.missing.asterisk";
049
050    @Override
051    public int[] getRequiredJavadocTokens() {
052        return new int[] {
053            JavadocCommentsTokenTypes.NEWLINE,
054        };
055    }
056
057    @Override
058    public int[] getAcceptableJavadocTokens() {
059        return getRequiredJavadocTokens();
060    }
061
062    @Override
063    public int[] getDefaultJavadocTokens() {
064        return getRequiredJavadocTokens();
065    }
066
067    @Override
068    public void visitJavadocToken(DetailNode detailNode) {
069        if (!isInsideHtmlComment(detailNode)) {
070            final DetailNode nextSibling = detailNode.getNextSibling();
071
072            if (nextSibling != null && !isLeadingAsterisk(nextSibling)
073                        && !isLastLine(nextSibling)) {
074                log(nextSibling.getLineNumber(), MSG_MISSING_ASTERISK);
075            }
076        }
077    }
078
079    /**
080     * Checks whether the given node is inside an HTML comment.
081     *
082     * @param detailNode the node to process
083     * @return {@code true} if the node is inside an HTML comment
084     */
085    private static boolean isInsideHtmlComment(DetailNode detailNode) {
086        final int parentType = detailNode.getParent().getType();
087        return parentType == JavadocCommentsTokenTypes.HTML_COMMENT_CONTENT
088                || parentType == JavadocCommentsTokenTypes.HTML_COMMENT;
089
090    }
091
092    /**
093     * Checks whether the given node is a leading asterisk.
094     *
095     * @param detailNode the node to process
096     * @return {@code true} if the node is {@link JavadocCommentsTokenTypes#LEADING_ASTERISK}
097     */
098    private static boolean isLeadingAsterisk(DetailNode detailNode) {
099        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}