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  import org.cactoos.text.Sub;
11  
12  /**
13   * Check indents in multi line JavaDoc tags.
14   *
15   * <p>This is how you should format javadoc tags that need a few lines:
16   *
17   * <pre>
18   * &#47;**
19   *  * This is my new method.
20   *  * &#64;param text Some text information, provided to the
21   *  *  method by another class
22   *  * &#64;todo #123 I will implement it later, when more information
23   *  *  come to light and I have documentation supplied by
24   *  *  AAA team in the office across the street
25   *  *&#47;
26   * public void func() {
27   *     // ...
28   * }
29   * </pre>
30   *
31   * <p>Keep in mind that all free-text information should go <b>before</b>
32   * javadoc tags, or else it will treated as part of the latest tag and
33   * qulice will complain.
34   *
35   * @since 0.3
36   */
37  public final class MultilineJavadocTagsCheck extends AbstractCheck {
38  
39      @Override
40      public int[] getDefaultTokens() {
41          return new int[] {
42              TokenTypes.METHOD_DEF,
43              TokenTypes.CTOR_DEF,
44              TokenTypes.PACKAGE_DEF,
45          };
46      }
47  
48      @Override
49      public int[] getAcceptableTokens() {
50          return this.getDefaultTokens();
51      }
52  
53      @Override
54      public int[] getRequiredTokens() {
55          return this.getDefaultTokens();
56      }
57  
58      @Override
59      public void visitToken(final DetailAST ast) {
60          final String[] lines = this.getLines();
61          final int start = ast.getLineNo();
62          final int cstart =
63              MultilineJavadocTagsCheck.findCommentStart(lines, start) + 1;
64          final int cend =
65              MultilineJavadocTagsCheck.findCommentEnd(lines, start) - 1;
66          if (cend >= cstart && cstart >= 0) {
67              this.checkJavaDoc(lines, cstart, cend);
68          } else {
69              this.log(0, "Can't find method comment");
70          }
71      }
72  
73      /**
74       * Checks method's Java Doc for satisfy indentation rules.
75       * @param lines Code of the whole class.
76       * @param start Start line of the Java Doc.
77       * @param end End line of the Java Doc.
78       */
79      @SuppressWarnings("PMD.InefficientEmptyStringCheck")
80      private void checkJavaDoc(final String[] lines, final int start,
81          final int end) {
82          boolean tagged = false;
83          int index = -1;
84          for (int current = start; current <= end; current += 1) {
85              final String line = lines[current];
86              if (line.contains("* @")) {
87                  tagged = true;
88                  index = line.indexOf('@');
89              } else {
90                  if (tagged) {
91                      final int comment = line.indexOf('*');
92                      final String sub = new Sub(
93                          line, comment + 1, index + 1
94                      ).toString();
95                      final String ext = new Sub(
96                          line, comment + 1, index + 2
97                      ).toString();
98                      if (!sub.trim().isEmpty() || ext.trim().isEmpty()) {
99                          this.log(
100                             current + 1,
101                             "Should contain one indentation space"
102                         );
103                     }
104                 }
105             }
106         }
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         final int doc = MultilineJavadocTagsCheck.findTrimmedTextUp(lines, start, "/**");
117         final int comment = MultilineJavadocTagsCheck.findTrimmedTextUp(lines, start, "/*");
118         return Math.max(doc, comment);
119     }
120 
121     /**
122      * Find javadoc ending comment.
123      * @param lines List of lines to check.
124      * @param start Start searching from this line number.
125      * @return Line number with found ending comment, or -1 if it wasn't found.
126      */
127     private static int findCommentEnd(final String[] lines, final int start) {
128         return MultilineJavadocTagsCheck.findTrimmedTextUp(lines, start, "*/");
129     }
130 
131     /**
132      * Find a text in lines, by going up.
133      * @param lines List of lines to check.
134      * @param start Start searching from this line number.
135      * @param text Text to find.
136      * @return Line number with found text, or -1 if it wasn't found.
137      */
138     private static int findTrimmedTextUp(final String[] lines,
139         final int start, final String text) {
140         int found = -1;
141         for (int pos = start - 1; pos >= 0; pos -= 1) {
142             if (lines[pos].trim().equals(text)) {
143                 found = pos;
144                 break;
145             }
146         }
147         return found;
148     }
149 }