View Javadoc
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 com.puppycrawl.tools.checkstyle.StatelessCheck;
23  import com.puppycrawl.tools.checkstyle.api.DetailNode;
24  import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes;
25  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
26  import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
27  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
28  
29  /**
30   * <p>
31   * Checks if the javadoc has
32   * <a href="https://docs.oracle.com/en/java/javase/14/docs/specs/javadoc/doc-comment-spec.html#leading-asterisks">
33   * leading asterisks</a> on each line.
34   * </p>
35   * <p>
36   * The check does not require asterisks on the first line, nor on the last line if it is blank.
37   * All other lines in a Javadoc should start with {@code *}, including blank lines and code blocks.
38   * </p>
39   * <ul>
40   * <li>
41   * Property {@code violateExecutionOnNonTightHtml} - Control when to print violations if the
42   * Javadoc being examined by this check violates the tight html rules defined at
43   * <a href="https://checkstyle.org/writingjavadocchecks.html#Tight-HTML_rules">Tight-HTML Rules</a>.
44   * Type is {@code boolean}.
45   * Default value is {@code false}.
46   * </li>
47   * </ul>
48   * <p>
49   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
50   * </p>
51   * <p>
52   * Violation Message Keys:
53   * </p>
54   * <ul>
55   * <li>
56   * {@code javadoc.missed.html.close}
57   * </li>
58   * <li>
59   * {@code javadoc.missing.asterisk}
60   * </li>
61   * <li>
62   * {@code javadoc.parse.rule.error}
63   * </li>
64   * <li>
65   * {@code javadoc.unclosedHtml}
66   * </li>
67   * <li>
68   * {@code javadoc.wrong.singleton.html.tag}
69   * </li>
70   * </ul>
71   *
72   * @since 8.38
73   */
74  @StatelessCheck
75  public class JavadocMissingLeadingAsteriskCheck extends AbstractJavadocCheck {
76  
77      /**
78       * A key is pointing to the warning message text in "messages.properties"
79       * file.
80       */
81      public static final String MSG_MISSING_ASTERISK = "javadoc.missing.asterisk";
82  
83      @Override
84      public int[] getRequiredJavadocTokens() {
85          return new int[] {
86              JavadocTokenTypes.NEWLINE,
87          };
88      }
89  
90      @Override
91      public int[] getAcceptableJavadocTokens() {
92          return getRequiredJavadocTokens();
93      }
94  
95      @Override
96      public int[] getDefaultJavadocTokens() {
97          return getRequiredJavadocTokens();
98      }
99  
100     @Override
101     public void visitJavadocToken(DetailNode detailNode) {
102         DetailNode nextSibling = getNextNode(detailNode);
103 
104         // Till https://github.com/checkstyle/checkstyle/issues/9005
105         // Due to bug in the Javadoc parser there may be phantom description nodes.
106         while (TokenUtil.isOfType(nextSibling.getType(),
107                 JavadocTokenTypes.DESCRIPTION, JavadocTokenTypes.WS)) {
108             nextSibling = getNextNode(nextSibling);
109         }
110 
111         if (!isLeadingAsterisk(nextSibling) && !isLastLine(nextSibling)) {
112             log(nextSibling.getLineNumber(), MSG_MISSING_ASTERISK);
113         }
114     }
115 
116     /**
117      * Gets next node in the ast (sibling or parent sibling for the last node).
118      *
119      * @param detailNode the node to process
120      * @return next node.
121      */
122     private static DetailNode getNextNode(DetailNode detailNode) {
123         DetailNode node = JavadocUtil.getFirstChild(detailNode);
124         if (node == null) {
125             node = JavadocUtil.getNextSibling(detailNode);
126             if (node == null) {
127                 DetailNode parent = detailNode;
128                 do {
129                     parent = parent.getParent();
130                     node = JavadocUtil.getNextSibling(parent);
131                 } while (node == null);
132             }
133         }
134         return node;
135     }
136 
137     /**
138      * Checks whether the given node is a leading asterisk.
139      *
140      * @param detailNode the node to process
141      * @return {@code true} if the node is {@link JavadocTokenTypes#LEADING_ASTERISK}
142      */
143     private static boolean isLeadingAsterisk(DetailNode detailNode) {
144         return detailNode.getType() == JavadocTokenTypes.LEADING_ASTERISK;
145     }
146 
147     /**
148      * Checks whether this node is the end of a Javadoc comment,
149      * optionally preceded by blank text.
150      *
151      * @param detailNode the node to process
152      * @return {@code true} if the node is {@link JavadocTokenTypes#EOF}
153      */
154     private static boolean isLastLine(DetailNode detailNode) {
155         final DetailNode node;
156         if (CommonUtil.isBlank(detailNode.getText())) {
157             node = getNextNode(detailNode);
158         }
159         else {
160             node = detailNode;
161         }
162         return node.getType() == JavadocTokenTypes.EOF;
163     }
164 
165 }