View Javadoc
1   /*
2    * Copyright (c) 2011-2025 Yegor Bugayenko
3    *
4    * All rights reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions
8    * are met: 1) Redistributions of source code must retain the above
9    * copyright notice, this list of conditions and the following
10   * disclaimer. 2) Redistributions in binary form must reproduce the above
11   * copyright notice, this list of conditions and the following
12   * disclaimer in the documentation and/or other materials provided
13   * with the distribution. 3) Neither the name of the Qulice.com nor
14   * the names of its contributors may be used to endorse or promote
15   * products derived from this software without specific prior written
16   * permission.
17   *
18   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
20   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29   * OF THE POSSIBILITY OF SUCH DAMAGE.
30   */
31  package com.qulice.checkstyle;
32  
33  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
34  import com.puppycrawl.tools.checkstyle.api.DetailAST;
35  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
36  import org.cactoos.text.Sub;
37  
38  /**
39   * Check indents in multi line JavaDoc tags.
40   *
41   * <p>This is how you should format javadoc tags that need a few lines:
42   *
43   * <pre>
44   * &#47;**
45   *  * This is my new method.
46   *  * &#64;param text Some text information, provided to the
47   *  *  method by another class
48   *  * &#64;todo #123 I will implement it later, when more information
49   *  *  come to light and I have documentation supplied by
50   *  *  AAA team in the office accross the street
51   *  *&#47;
52   * public void func() {
53   *     // ...
54   * }
55   * </pre>
56   *
57   * <p>Keep in mind that all free-text information should go <b>before</b>
58   * javadoc tags, or else it will treated as part of the latest tag and
59   * qulice will complain.
60   *
61   * @since 0.3
62   */
63  public final class MultilineJavadocTagsCheck extends AbstractCheck {
64  
65      @Override
66      public int[] getDefaultTokens() {
67          return new int[] {
68              TokenTypes.METHOD_DEF,
69              TokenTypes.CTOR_DEF,
70              TokenTypes.PACKAGE_DEF,
71          };
72      }
73  
74      @Override
75      public int[] getAcceptableTokens() {
76          return this.getDefaultTokens();
77      }
78  
79      @Override
80      public int[] getRequiredTokens() {
81          return this.getDefaultTokens();
82      }
83  
84      @Override
85      public void visitToken(final DetailAST ast) {
86          final String[] lines = this.getLines();
87          final int start = ast.getLineNo();
88          final int cstart =
89              MultilineJavadocTagsCheck.findCommentStart(lines, start) + 1;
90          final int cend =
91              MultilineJavadocTagsCheck.findCommentEnd(lines, start) - 1;
92          if (cend >= cstart && cstart >= 0) {
93              this.checkJavaDoc(lines, cstart, cend);
94          } else {
95              this.log(0, "Can't find method comment");
96          }
97      }
98  
99      /**
100      * Checks method's Java Doc for satisfy indentation rules.
101      * @param lines Code of the whole class.
102      * @param start Start line of the Java Doc.
103      * @param end End line of the Java Doc.
104      */
105     @SuppressWarnings("PMD.InefficientEmptyStringCheck")
106     private void checkJavaDoc(final String[] lines, final int start,
107         final int end) {
108         boolean tagged = false;
109         int index = -1;
110         for (int current = start; current <= end; current += 1) {
111             final String line = lines[current];
112             if (line.contains("* @")) {
113                 tagged = true;
114                 index = line.indexOf('@');
115             } else {
116                 if (tagged) {
117                     final int comment = line.indexOf('*');
118                     final String sub = new Sub(
119                         line, comment + 1, index + 1
120                     ).toString();
121                     final String ext = new Sub(
122                         line, comment + 1, index + 2
123                     ).toString();
124                     if (!sub.trim().isEmpty() || ext.trim().isEmpty()) {
125                         this.log(
126                             current + 1,
127                             "Should contain one indentation space"
128                         );
129                     }
130                 }
131             }
132         }
133     }
134 
135     /**
136      * Find javadoc starting comment.
137      * @param lines List of lines to check.
138      * @param start Start searching from this line number.
139      * @return Line number with found starting comment or -1 otherwise.
140      */
141     private static int findCommentStart(final String[] lines, final int start) {
142         return MultilineJavadocTagsCheck.findTrimmedTextUp(lines, start, "/**");
143     }
144 
145     /**
146      * Find javadoc ending comment.
147      * @param lines List of lines to check.
148      * @param start Start searching from this line number.
149      * @return Line number with found ending comment, or -1 if it wasn't found.
150      */
151     private static int findCommentEnd(final String[] lines, final int start) {
152         return MultilineJavadocTagsCheck.findTrimmedTextUp(lines, start, "*/");
153     }
154 
155     /**
156      * Find a text in lines, by going up.
157      * @param lines List of lines to check.
158      * @param start Start searching from this line number.
159      * @param text Text to find.
160      * @return Line number with found text, or -1 if it wasn't found.
161      */
162     private static int findTrimmedTextUp(final String[] lines,
163         final int start, final String text) {
164         int found = -1;
165         for (int pos = start - 1; pos >= 0; pos -= 1) {
166             if (lines[pos].trim().equals(text)) {
167                 found = pos;
168                 break;
169             }
170         }
171         return found;
172     }
173 }