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.JavadocUtil;
26  
27  /**
28   * <p>
29   * Checks that there is at least one whitespace after the leading asterisk.
30   * Although spaces after asterisks are optional in the Javadoc comments, their absence
31   * makes the documentation difficult to read. It is the de facto standard to put at least
32   * one whitespace after the leading asterisk.
33   * </p>
34   * <ul>
35   * <li>
36   * Property {@code violateExecutionOnNonTightHtml} - Control when to print violations
37   * if the Javadoc being examined by this check violates the tight html rules defined at
38   * <a href="https://checkstyle.org/writingjavadocchecks.html#Tight-HTML_rules">Tight-HTML Rules</a>.
39   * Type is {@code boolean}.
40   * Default value is {@code false}.
41   * </li>
42   * </ul>
43   * <p>
44   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
45   * </p>
46   * <p>
47   * Violation Message Keys:
48   * </p>
49   * <ul>
50   * <li>
51   * {@code javadoc.missed.html.close}
52   * </li>
53   * <li>
54   * {@code javadoc.missing.whitespace}
55   * </li>
56   * <li>
57   * {@code javadoc.parse.rule.error}
58   * </li>
59   * <li>
60   * {@code javadoc.unclosedHtml}
61   * </li>
62   * <li>
63   * {@code javadoc.wrong.singleton.html.tag}
64   * </li>
65   * </ul>
66   *
67   * @since 8.32
68   */
69  @StatelessCheck
70  public class JavadocMissingWhitespaceAfterAsteriskCheck extends AbstractJavadocCheck {
71  
72      /**
73       * A key is pointing to the warning message text in "messages.properties" file.
74       */
75      public static final String MSG_KEY = "javadoc.missing.whitespace";
76  
77      @Override
78      public int[] getDefaultJavadocTokens() {
79          return new int[] {
80              JavadocTokenTypes.JAVADOC,
81              JavadocTokenTypes.LEADING_ASTERISK,
82          };
83      }
84  
85      @Override
86      public int[] getRequiredJavadocTokens() {
87          return getAcceptableJavadocTokens();
88      }
89  
90      @Override
91      public void visitJavadocToken(DetailNode detailNode) {
92          final DetailNode textNode;
93  
94          if (detailNode.getType() == JavadocTokenTypes.JAVADOC) {
95              textNode = JavadocUtil.getFirstChild(detailNode);
96          }
97          else {
98              textNode = JavadocUtil.getNextSibling(detailNode);
99          }
100 
101         if (textNode != null && textNode.getType() != JavadocTokenTypes.EOF) {
102             final String text = textNode.getText();
103             final int lastAsteriskPosition = getLastLeadingAsteriskPosition(text);
104 
105             if (!isLast(lastAsteriskPosition, text)
106                     && !Character.isWhitespace(text.charAt(lastAsteriskPosition + 1))) {
107                 log(textNode.getLineNumber(), textNode.getColumnNumber(), MSG_KEY);
108             }
109         }
110     }
111 
112     /**
113      * Checks if the character position is the last one of the string.
114      *
115      * @param position the position of the character
116      * @param text String literal.
117      * @return true if the character position is the last one of the string.
118      *
119      */
120     private static boolean isLast(int position, String text) {
121         return position == text.length() - 1;
122     }
123 
124     /**
125      * Finds the position of the last leading asterisk in the string.
126      * If {@code text} contains no leading asterisk, -1 will be returned.
127      *
128      * @param text String literal.
129      * @return the index of the last leading asterisk.
130      *
131      */
132     private static int getLastLeadingAsteriskPosition(String text) {
133         int index = -1;
134 
135         for (int i = 0; i < text.length(); i++) {
136             if (text.charAt(i) != '*') {
137                 break;
138             }
139             index++;
140         }
141 
142         return index;
143     }
144 
145 }