001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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.annotation;
021
022import com.puppycrawl.tools.checkstyle.StatelessCheck;
023import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
027
028/**
029 * <p>
030 * Checks that annotations are located on the same line with their targets.
031 * Verifying with this check is not good practice, but it is using by some style guides.
032 * </p>
033 * <ul>
034 * <li>
035 * Property {@code tokens} - tokens to check
036 * Default value is:
037 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
038 * CLASS_DEF</a>,
039 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
040 * INTERFACE_DEF</a>,
041 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
042 * ENUM_DEF</a>,
043 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
044 * METHOD_DEF</a>,
045 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
046 * CTOR_DEF</a>,
047 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
048 * VARIABLE_DEF</a>.
049 * </li>
050 * </ul>
051 * <p>
052 * To configure the check:
053 * </p>
054 * <pre>
055 * &lt;module name=&quot;AnnotationOnSameLine&quot;/&gt;
056 * </pre>
057 * <p>
058 * Example to allow annotations on the same line
059 * </p>
060 * <pre>
061 * &#64;Override public int toString() { ... } // no violations
062 * &#64;Before &#64;Override public void set() { ... } // no violation
063 * </pre>
064 * <p>
065 * Example to disallow annotations on previous line
066 * </p>
067 * <pre>
068 * &#64;SuppressWarnings("deprecation") // violation
069 * &#64;Override // violation
070 * public int foo() { ... }
071 * </pre>
072 *
073 * @since 8.2
074 */
075@StatelessCheck
076public class AnnotationOnSameLineCheck extends AbstractCheck {
077
078    /** A key is pointing to the warning message text in "messages.properties" file. */
079    public static final String MSG_KEY_ANNOTATION_ON_SAME_LINE = "annotation.same.line";
080
081    @Override
082    public int[] getDefaultTokens() {
083        return new int[] {
084            TokenTypes.CLASS_DEF,
085            TokenTypes.INTERFACE_DEF,
086            TokenTypes.ENUM_DEF,
087            TokenTypes.METHOD_DEF,
088            TokenTypes.CTOR_DEF,
089            TokenTypes.VARIABLE_DEF,
090        };
091    }
092
093    @Override
094    public int[] getAcceptableTokens() {
095        return new int[] {
096            TokenTypes.CLASS_DEF,
097            TokenTypes.INTERFACE_DEF,
098            TokenTypes.ENUM_DEF,
099            TokenTypes.METHOD_DEF,
100            TokenTypes.CTOR_DEF,
101            TokenTypes.VARIABLE_DEF,
102            TokenTypes.PARAMETER_DEF,
103            TokenTypes.ANNOTATION_DEF,
104            TokenTypes.TYPECAST,
105            TokenTypes.LITERAL_THROWS,
106            TokenTypes.IMPLEMENTS_CLAUSE,
107            TokenTypes.TYPE_ARGUMENT,
108            TokenTypes.LITERAL_NEW,
109            TokenTypes.DOT,
110            TokenTypes.ANNOTATION_FIELD_DEF,
111        };
112    }
113
114    @Override
115    public int[] getRequiredTokens() {
116        return CommonUtil.EMPTY_INT_ARRAY;
117    }
118
119    @Override
120    public void visitToken(DetailAST ast) {
121        DetailAST nodeWithAnnotations = ast;
122        if (ast.getType() == TokenTypes.TYPECAST) {
123            nodeWithAnnotations = ast.findFirstToken(TokenTypes.TYPE);
124        }
125        DetailAST modifiersNode = nodeWithAnnotations.findFirstToken(TokenTypes.MODIFIERS);
126        if (modifiersNode == null) {
127            modifiersNode = nodeWithAnnotations.findFirstToken(TokenTypes.ANNOTATIONS);
128        }
129        if (modifiersNode != null) {
130            for (DetailAST annotationNode = modifiersNode.getFirstChild();
131                    annotationNode != null;
132                    annotationNode = annotationNode.getNextSibling()) {
133                if (annotationNode.getType() == TokenTypes.ANNOTATION
134                        && annotationNode.getLineNo() != getNextNode(annotationNode).getLineNo()) {
135                    log(annotationNode.getLineNo(), MSG_KEY_ANNOTATION_ON_SAME_LINE,
136                          getAnnotationName(annotationNode));
137                }
138            }
139        }
140    }
141
142    /**
143     * Finds next node of ast tree.
144     * @param node current node
145     * @return node that is next to given
146     */
147    private static DetailAST getNextNode(DetailAST node) {
148        DetailAST nextNode = node.getNextSibling();
149        if (nextNode == null) {
150            nextNode = node.getParent().getNextSibling();
151        }
152        return nextNode;
153    }
154
155    /**
156     * Returns the name of the given annotation.
157     * @param annotation annotation node.
158     * @return annotation name.
159     */
160    private static String getAnnotationName(DetailAST annotation) {
161        DetailAST identNode = annotation.findFirstToken(TokenTypes.IDENT);
162        if (identNode == null) {
163            identNode = annotation.findFirstToken(TokenTypes.DOT).getLastChild();
164        }
165        return identNode.getText();
166    }
167
168}