View Javadoc
1   /*
2    * Copyright (c) 2011-2024 Qulice.com
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          };
71      }
72  
73      @Override
74      public int[] getAcceptableTokens() {
75          return this.getDefaultTokens();
76      }
77  
78      @Override
79      public int[] getRequiredTokens() {
80          return this.getDefaultTokens();
81      }
82  
83      @Override
84      public void visitToken(final DetailAST ast) {
85          final String[] lines = this.getLines();
86          final int start = ast.getLineNo();
87          final int cstart =
88              MultilineJavadocTagsCheck.findCommentStart(lines, start) + 1;
89          final int cend =
90              MultilineJavadocTagsCheck.findCommentEnd(lines, start) - 1;
91          if (cend >= cstart && cstart >= 0) {
92              this.checkJavaDoc(lines, cstart, cend);
93          } else {
94              this.log(0, "Can't find method comment");
95          }
96      }
97  
98      /**
99       * Checks method's Java Doc for satisfy indentation rules.
100      * @param lines Code of the whole class.
101      * @param start Start line of the Java Doc.
102      * @param end End line of the Java Doc.
103      */
104     @SuppressWarnings("PMD.InefficientEmptyStringCheck")
105     private void checkJavaDoc(final String[] lines, final int start,
106         final int end) {
107         boolean tagged = false;
108         int index = -1;
109         for (int current = start; current <= end; current += 1) {
110             final String line = lines[current];
111             if (line.contains("* @")) {
112                 tagged = true;
113                 index = line.indexOf('@');
114             } else {
115                 if (tagged) {
116                     final int comment = line.indexOf('*');
117                     final String sub = new Sub(
118                         line, comment + 1, index + 1
119                     ).toString();
120                     final String ext = new Sub(
121                         line, comment + 1, index + 2
122                     ).toString();
123                     if (!sub.trim().isEmpty() || ext.trim().isEmpty()) {
124                         this.log(
125                             current + 1,
126                             "Should contain one indentation space"
127                         );
128                     }
129                 }
130             }
131         }
132     }
133 
134     /**
135      * Find javadoc starting comment.
136      * @param lines List of lines to check.
137      * @param start Start searching from this line number.
138      * @return Line number with found starting comment or -1 otherwise.
139      */
140     private static int findCommentStart(final String[] lines, final int start) {
141         return MultilineJavadocTagsCheck.findTrimmedTextUp(lines, start, "/**");
142     }
143 
144     /**
145      * Find javadoc ending comment.
146      * @param lines List of lines to check.
147      * @param start Start searching from this line number.
148      * @return Line number with found ending comment, or -1 if it wasn't found.
149      */
150     private static int findCommentEnd(final String[] lines, final int start) {
151         return MultilineJavadocTagsCheck.findTrimmedTextUp(lines, start, "*/");
152     }
153 
154     /**
155      * Find a text in lines, by going up.
156      * @param lines List of lines to check.
157      * @param start Start searching from this line number.
158      * @param text Text to find.
159      * @return Line number with found text, or -1 if it wasn't found.
160      */
161     private static int findTrimmedTextUp(final String[] lines,
162         final int start, final String text) {
163         int found = -1;
164         for (int pos = start - 1; pos >= 0; pos -= 1) {
165             if (lines[pos].trim().equals(text)) {
166                 found = pos;
167                 break;
168             }
169         }
170         return found;
171     }
172 }