View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2025 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.checkstyle;
6   
7   import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
8   import com.puppycrawl.tools.checkstyle.api.DetailAST;
9   import com.puppycrawl.tools.checkstyle.api.TokenTypes;
10  
11  /**
12   * Check for empty line at the beginning and at the end of Javadoc.
13   *
14   * <p>You can't have empty line at the beginning or at the end of Javadoc.
15   *
16   * <p>The following red lines in class Javadoc will be reported as violations.
17   * <pre>
18   * &#47;**
19   *  <span style="color:red" >*</span>
20   *  * This is my class.
21   *  <span style="color:red" >*</span>
22   *  *&#47;
23   * public final class Foo {
24   *     // ...
25   * </pre>
26   *
27   * @since 0.17
28   */
29  public final class JavadocEmptyLineCheck extends AbstractCheck {
30  
31      @Override
32      public int[] getDefaultTokens() {
33          return new int[] {
34              TokenTypes.PACKAGE_DEF,
35              TokenTypes.CLASS_DEF,
36              TokenTypes.INTERFACE_DEF,
37              TokenTypes.ANNOTATION_DEF,
38              TokenTypes.ANNOTATION_FIELD_DEF,
39              TokenTypes.ENUM_DEF,
40              TokenTypes.ENUM_CONSTANT_DEF,
41              TokenTypes.VARIABLE_DEF,
42              TokenTypes.CTOR_DEF,
43              TokenTypes.METHOD_DEF,
44          };
45      }
46  
47      @Override
48      public int[] getAcceptableTokens() {
49          return this.getDefaultTokens();
50      }
51  
52      @Override
53      public int[] getRequiredTokens() {
54          return this.getDefaultTokens();
55      }
56  
57      @Override
58      public void visitToken(final DetailAST ast) {
59          final String[] lines = this.getLines();
60          final int current = ast.getLineNo();
61          final int start =
62              JavadocEmptyLineCheck.findCommentStart(lines, current) + 1;
63          if (JavadocEmptyLineCheck.isNodeHavingJavadoc(ast, start)) {
64              if (JavadocEmptyLineCheck.isJavadocLineEmpty(lines[start])) {
65                  this.log(start + 1, "Empty Javadoc line at the beginning");
66              }
67              final int end =
68                  JavadocEmptyLineCheck.findCommentEnd(lines, current) - 1;
69              if (JavadocEmptyLineCheck.isJavadocLineEmpty(lines[end])) {
70                  this.log(end + 1, "Empty Javadoc line at the end");
71              }
72          }
73      }
74  
75      /**
76       * Check if Javadoc line is empty.
77       * @param line Javadoc line
78       * @return True when Javadoc line is empty
79       */
80      private static boolean isJavadocLineEmpty(final String line) {
81          return "*".equals(line.trim());
82      }
83  
84      /**
85       * Check if node has Javadoc.
86       * @param node Node to be checked for Javadoc.
87       * @param start Line number where comment starts.
88       * @return True when node has Javadoc
89       */
90      private static boolean isNodeHavingJavadoc(final DetailAST node,
91          final int start) {
92          return start > getLineNoOfPreviousNode(node);
93      }
94  
95      /**
96       * Returns line number of previous node.
97       * @param node Current node.
98       * @return Line number of previous node
99       */
100     private static int getLineNoOfPreviousNode(final DetailAST node) {
101         int start = 0;
102         final DetailAST previous = node.getPreviousSibling();
103         if (previous != null) {
104             start = previous.getLineNo();
105         }
106         return start;
107     }
108 
109     /**
110      * Find Javadoc starting comment.
111      * @param lines List of lines to check.
112      * @param start Start searching from this line number.
113      * @return Line number with found starting comment or -1 otherwise.
114      */
115     private static int findCommentStart(final String[] lines, final int start) {
116         return JavadocEmptyLineCheck.findTrimmedTextUp(lines, start, "/**");
117     }
118 
119     /**
120      * Find Javadoc ending comment.
121      * @param lines Array of lines to check.
122      * @param start Start searching from this line number.
123      * @return Line number with found ending comment, or -1 if it wasn't found.
124      */
125     private static int findCommentEnd(final String[] lines, final int start) {
126         return JavadocEmptyLineCheck.findTrimmedTextUp(lines, start, "*/");
127     }
128 
129     /**
130      * Find a text in lines, by going up.
131      * @param lines Array of lines to check.
132      * @param start Start searching from this line number.
133      * @param text Text to find.
134      * @return Line number with found text, or -1 if it wasn't found.
135      */
136     private static int findTrimmedTextUp(final String[] lines,
137         final int start, final String text) {
138         int found = -1;
139         for (int pos = start - 1; pos >= 0; pos -= 1) {
140             if (lines[pos].trim().equals(text)) {
141                 found = pos;
142                 break;
143             }
144         }
145         return found;
146     }
147 }