1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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 java.util.ArrayList;
23  import java.util.List;
24  
25  import com.puppycrawl.tools.checkstyle.StatelessCheck;
26  import com.puppycrawl.tools.checkstyle.api.DetailNode;
27  import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes;
28  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
29  import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
30  
31  /**
32   * <p>
33   * Checks the indentation of the continuation lines in at-clauses.
34   * </p>
35   * <p>
36   * Default configuration:
37   * </p>
38   * <pre>
39   * &lt;module name=&quot;JavadocTagContinuationIndentation&quot;&gt;
40   *     &lt;property name=&quot;offset&quot; value=&quot;4&quot;/&gt;
41   * &lt;/module&gt;
42   * </pre>
43   *
44   *
45   */
46  @StatelessCheck
47  public class JavadocTagContinuationIndentationCheck extends AbstractJavadocCheck {
48  
49      /**
50       * A key is pointing to the warning message text in "messages.properties"
51       * file.
52       */
53      public static final String MSG_KEY = "tag.continuation.indent";
54  
55      /** Default tag continuation indentation. */
56      private static final int DEFAULT_INDENTATION = 4;
57  
58      /**
59       * How many spaces to use for new indentation level.
60       */
61      private int offset = DEFAULT_INDENTATION;
62  
63      /**
64       * Sets custom indentation level.
65       * @param offset custom value.
66       */
67      public void setOffset(int offset) {
68          this.offset = offset;
69      }
70  
71      @Override
72      public int[] getDefaultJavadocTokens() {
73          return new int[] {JavadocTokenTypes.DESCRIPTION };
74      }
75  
76      @Override
77      public int[] getRequiredJavadocTokens() {
78          return getAcceptableJavadocTokens();
79      }
80  
81      @Override
82      public void visitJavadocToken(DetailNode ast) {
83          if (!isInlineDescription(ast)) {
84              final List<DetailNode> textNodes = getAllNewlineNodes(ast);
85              for (DetailNode newlineNode : textNodes) {
86                  final DetailNode textNode = JavadocUtil.getNextSibling(JavadocUtil
87                          .getNextSibling(newlineNode));
88                  if (textNode != null && textNode.getType() == JavadocTokenTypes.TEXT) {
89                      final String text = textNode.getText();
90                      if (!CommonUtil.isBlank(text.trim())
91                              && (text.length() <= offset
92                                      || !text.substring(1, offset + 1).trim().isEmpty())) {
93                          log(textNode.getLineNumber(), MSG_KEY, offset);
94                      }
95                  }
96              }
97          }
98      }
99  
100     /**
101      * Finds and collects all NEWLINE nodes inside DESCRIPTION node.
102      * @param descriptionNode DESCRIPTION node.
103      * @return List with NEWLINE nodes.
104      */
105     private static List<DetailNode> getAllNewlineNodes(DetailNode descriptionNode) {
106         final List<DetailNode> textNodes = new ArrayList<>();
107         DetailNode node = JavadocUtil.getFirstChild(descriptionNode);
108         while (JavadocUtil.getNextSibling(node) != null) {
109             if (node.getType() == JavadocTokenTypes.NEWLINE) {
110                 textNodes.add(node);
111             }
112             node = JavadocUtil.getNextSibling(node);
113         }
114         return textNodes;
115     }
116 
117     /**
118      * Checks, if description node is a description of in-line tag.
119      * @param description DESCRIPTION node.
120      * @return true, if description node is a description of in-line tag.
121      */
122     private static boolean isInlineDescription(DetailNode description) {
123         boolean isInline = false;
124         DetailNode inlineTag = description.getParent();
125         while (inlineTag != null) {
126             if (inlineTag.getType() == JavadocTokenTypes.JAVADOC_INLINE_TAG) {
127                 isInline = true;
128                 break;
129             }
130             inlineTag = inlineTag.getParent();
131         }
132         return isInline;
133     }
134 
135 }