001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2018 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.javadoc;
021
022import java.util.ArrayList;
023import java.util.List;
024
025import com.puppycrawl.tools.checkstyle.api.DetailNode;
026import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes;
027import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
028import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
029
030/**
031 * <p>
032 * Checks the indentation of the continuation lines in at-clauses.
033 * </p>
034 * <p>
035 * Default configuration:
036 * </p>
037 * <pre>
038 * &lt;module name=&quot;JavadocTagContinuationIndentation&quot;&gt;
039 *     &lt;property name=&quot;offset&quot; value=&quot;4&quot;/&gt;
040 * &lt;/module&gt;
041 * </pre>
042 *
043 *
044 */
045public class JavadocTagContinuationIndentationCheck extends AbstractJavadocCheck {
046
047    /**
048     * A key is pointing to the warning message text in "messages.properties"
049     * file.
050     */
051    public static final String MSG_KEY = "tag.continuation.indent";
052
053    /** Default tag continuation indentation. */
054    private static final int DEFAULT_INDENTATION = 4;
055
056    /**
057     * How many spaces to use for new indentation level.
058     */
059    private int offset = DEFAULT_INDENTATION;
060
061    /**
062     * Sets custom indentation level.
063     * @param offset custom value.
064     */
065    public void setOffset(int offset) {
066        this.offset = offset;
067    }
068
069    @Override
070    public int[] getDefaultJavadocTokens() {
071        return new int[] {JavadocTokenTypes.DESCRIPTION };
072    }
073
074    @Override
075    public int[] getRequiredJavadocTokens() {
076        return getAcceptableJavadocTokens();
077    }
078
079    @Override
080    public void visitJavadocToken(DetailNode ast) {
081        if (!isInlineDescription(ast)) {
082            final List<DetailNode> textNodes = getAllNewlineNodes(ast);
083            for (DetailNode newlineNode : textNodes) {
084                final DetailNode textNode = JavadocUtil.getNextSibling(JavadocUtil
085                        .getNextSibling(newlineNode));
086                if (textNode != null && textNode.getType() == JavadocTokenTypes.TEXT) {
087                    final String text = textNode.getText();
088                    if (!CommonUtil.isBlank(text.trim())
089                            && (text.length() <= offset
090                                    || !text.substring(1, offset + 1).trim().isEmpty())) {
091                        log(textNode.getLineNumber(), MSG_KEY, offset);
092                    }
093                }
094            }
095        }
096    }
097
098    /**
099     * Finds and collects all NEWLINE nodes inside DESCRIPTION node.
100     * @param descriptionNode DESCRIPTION node.
101     * @return List with NEWLINE nodes.
102     */
103    private static List<DetailNode> getAllNewlineNodes(DetailNode descriptionNode) {
104        final List<DetailNode> textNodes = new ArrayList<>();
105        DetailNode node = JavadocUtil.getFirstChild(descriptionNode);
106        while (JavadocUtil.getNextSibling(node) != null) {
107            if (node.getType() == JavadocTokenTypes.NEWLINE) {
108                textNodes.add(node);
109            }
110            node = JavadocUtil.getNextSibling(node);
111        }
112        return textNodes;
113    }
114
115    /**
116     * Checks, if description node is a description of in-line tag.
117     * @param description DESCRIPTION node.
118     * @return true, if description node is a description of in-line tag.
119     */
120    private static boolean isInlineDescription(DetailNode description) {
121        boolean isInline = false;
122        DetailNode inlineTag = description.getParent();
123        while (inlineTag != null) {
124            if (inlineTag.getType() == JavadocTokenTypes.JAVADOC_INLINE_TAG) {
125                isInline = true;
126                break;
127            }
128            inlineTag = inlineTag.getParent();
129        }
130        return isInline;
131    }
132
133}