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   * <div>
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   * </div>
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   *
44   * <p>
45   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
46   * </p>
47   *
48   * <p>
49   * Violation Message Keys:
50   * </p>
51   * <ul>
52   * <li>
53   * {@code javadoc.missed.html.close}
54   * </li>
55   * <li>
56   * {@code javadoc.missing.whitespace}
57   * </li>
58   * <li>
59   * {@code javadoc.parse.rule.error}
60   * </li>
61   * <li>
62   * {@code javadoc.unclosedHtml}
63   * </li>
64   * <li>
65   * {@code javadoc.wrong.singleton.html.tag}
66   * </li>
67   * </ul>
68   *
69   * @since 8.32
70   */
71  @StatelessCheck
72  public class JavadocMissingWhitespaceAfterAsteriskCheck extends AbstractJavadocCheck {
73  
74      /**
75       * A key is pointing to the warning message text in "messages.properties" file.
76       */
77      public static final String MSG_KEY = "javadoc.missing.whitespace";
78  
79      @Override
80      public int[] getDefaultJavadocTokens() {
81          return new int[] {
82              JavadocTokenTypes.JAVADOC,
83              JavadocTokenTypes.LEADING_ASTERISK,
84          };
85      }
86  
87      @Override
88      public int[] getRequiredJavadocTokens() {
89          return getAcceptableJavadocTokens();
90      }
91  
92      @Override
93      public void visitJavadocToken(DetailNode detailNode) {
94          final DetailNode textNode;
95  
96          if (detailNode.getType() == JavadocTokenTypes.JAVADOC) {
97              textNode = JavadocUtil.getFirstChild(detailNode);
98          }
99          else {
100             textNode = JavadocUtil.getNextSibling(detailNode);
101         }
102 
103         if (textNode != null && textNode.getType() != JavadocTokenTypes.EOF) {
104             final String text = textNode.getText();
105             final int lastAsteriskPosition = getLastLeadingAsteriskPosition(text);
106 
107             if (!isLast(lastAsteriskPosition, text)
108                     && !Character.isWhitespace(text.charAt(lastAsteriskPosition + 1))) {
109                 log(textNode.getLineNumber(), textNode.getColumnNumber(), MSG_KEY);
110             }
111         }
112     }
113 
114     /**
115      * Checks if the character position is the last one of the string.
116      *
117      * @param position the position of the character
118      * @param text String literal.
119      * @return true if the character position is the last one of the string.
120      *
121      */
122     private static boolean isLast(int position, String text) {
123         return position == text.length() - 1;
124     }
125 
126     /**
127      * Finds the position of the last leading asterisk in the string.
128      * If {@code text} contains no leading asterisk, -1 will be returned.
129      *
130      * @param text String literal.
131      * @return the index of the last leading asterisk.
132      *
133      */
134     private static int getLastLeadingAsteriskPosition(String text) {
135         int index = -1;
136 
137         for (int i = 0; i < text.length(); i++) {
138             if (text.charAt(i) != '*') {
139                 break;
140             }
141             index++;
142         }
143 
144         return index;
145     }
146 
147 }