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;
027import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
028
029/**
030 * <p>
031 * Checks that annotations are located on the same line with their targets.
032 * Verifying with this check is not good practice, but it is using by some style guides.
033 * </p>
034 * <ul>
035 * <li>
036 * Property {@code tokens} - tokens to check
037 * Type is {@code int[]}.
038 * Default value is:
039 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
040 * CLASS_DEF</a>,
041 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
042 * INTERFACE_DEF</a>,
043 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
044 * ENUM_DEF</a>,
045 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
046 * METHOD_DEF</a>,
047 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
048 * CTOR_DEF</a>,
049 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
050 * VARIABLE_DEF</a>.
051 * </li>
052 * </ul>
053 * <p>
054 * To configure the check:
055 * </p>
056 * <pre>
057 * &lt;module name=&quot;AnnotationOnSameLine&quot;/&gt;
058 * </pre>
059 * <p>
060 * Example:
061 * </p>
062 * <pre>
063 * class Foo {
064 *
065 *   &#64;SuppressWarnings("deprecation")  // violation, annotation should be on the same line
066 *   public Foo() {
067 *   }
068 *
069 *   &#64;SuppressWarnings("unchecked") public void fun2() {  // OK
070 *   }
071 *
072 * }
073 *
074 * &#64;SuppressWarnings("unchecked") class Bar extends Foo {  // OK
075 *
076 *   &#64;Deprecated public Bar() {  // OK
077 *   }
078 *
079 *   &#64;Override  // violation, annotation should be on the same line
080 *   public void fun1() {
081 *   }
082 *
083 *   &#64;Before &#64;Override public void fun2() {  // OK
084 *   }
085 *
086 *   &#64;SuppressWarnings("deprecation")  // violation, annotation should be on the same line
087 *   &#64;Before public void fun3() {
088 *   }
089 *
090 * }
091 * </pre>
092 * <p>
093 * To configure the check to check for annotations applied on
094 * interfaces, variables and constructors:
095 * </p>
096 * <pre>
097 * &lt;module name=&quot;AnnotationOnSameLine&quot;&gt;
098 *   &lt;property name=&quot;tokens&quot;
099 *       value=&quot;INTERFACE_DEF, VARIABLE_DEF, CTOR_DEF&quot;/&gt;
100 * &lt;/module&gt;
101 * </pre>
102 * <p>
103 * Example:
104 * </p>
105 * <pre>
106 * &#64;Deprecated interface Foo {  // OK
107 *
108 *   void doSomething();
109 *
110 * }
111 *
112 * class Bar implements Foo {
113 *
114 *   &#64;SuppressWarnings("deprecation")  // violation, annotation should be on the same line
115 *   public Bar() {
116 *   }
117 *
118 *   &#64;Override  // OK
119 *   public void doSomething() {
120 *   }
121 *
122 *   &#64;Nullable  // violation, annotation should be on the same line
123 *   String s;
124 *
125 * }
126 * </pre>
127 * <p>
128 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
129 * </p>
130 * <p>
131 * Violation Message Keys:
132 * </p>
133 * <ul>
134 * <li>
135 * {@code annotation.same.line}
136 * </li>
137 * </ul>
138 *
139 * @since 8.2
140 */
141@StatelessCheck
142public class AnnotationOnSameLineCheck extends AbstractCheck {
143
144    /** A key is pointing to the warning message text in "messages.properties" file. */
145    public static final String MSG_KEY_ANNOTATION_ON_SAME_LINE = "annotation.same.line";
146
147    @Override
148    public int[] getDefaultTokens() {
149        return new int[] {
150            TokenTypes.CLASS_DEF,
151            TokenTypes.INTERFACE_DEF,
152            TokenTypes.ENUM_DEF,
153            TokenTypes.METHOD_DEF,
154            TokenTypes.CTOR_DEF,
155            TokenTypes.VARIABLE_DEF,
156        };
157    }
158
159    @Override
160    public int[] getAcceptableTokens() {
161        return new int[] {
162            TokenTypes.CLASS_DEF,
163            TokenTypes.INTERFACE_DEF,
164            TokenTypes.ENUM_DEF,
165            TokenTypes.METHOD_DEF,
166            TokenTypes.CTOR_DEF,
167            TokenTypes.VARIABLE_DEF,
168            TokenTypes.PARAMETER_DEF,
169            TokenTypes.ANNOTATION_DEF,
170            TokenTypes.TYPECAST,
171            TokenTypes.LITERAL_THROWS,
172            TokenTypes.IMPLEMENTS_CLAUSE,
173            TokenTypes.TYPE_ARGUMENT,
174            TokenTypes.LITERAL_NEW,
175            TokenTypes.DOT,
176            TokenTypes.ANNOTATION_FIELD_DEF,
177        };
178    }
179
180    @Override
181    public int[] getRequiredTokens() {
182        return CommonUtil.EMPTY_INT_ARRAY;
183    }
184
185    @Override
186    public void visitToken(DetailAST ast) {
187        DetailAST nodeWithAnnotations = ast;
188        if (ast.getType() == TokenTypes.TYPECAST) {
189            nodeWithAnnotations = ast.findFirstToken(TokenTypes.TYPE);
190        }
191        DetailAST modifiersNode = nodeWithAnnotations.findFirstToken(TokenTypes.MODIFIERS);
192        if (modifiersNode == null) {
193            modifiersNode = nodeWithAnnotations.findFirstToken(TokenTypes.ANNOTATIONS);
194        }
195        if (modifiersNode != null) {
196            for (DetailAST annotationNode = modifiersNode.getFirstChild();
197                    annotationNode != null;
198                    annotationNode = annotationNode.getNextSibling()) {
199                if (annotationNode.getType() == TokenTypes.ANNOTATION
200                        && !TokenUtil.areOnSameLine(annotationNode, getNextNode(annotationNode))) {
201                    log(annotationNode, MSG_KEY_ANNOTATION_ON_SAME_LINE,
202                          getAnnotationName(annotationNode));
203                }
204            }
205        }
206    }
207
208    /**
209     * Finds next node of ast tree.
210     *
211     * @param node current node
212     * @return node that is next to given
213     */
214    private static DetailAST getNextNode(DetailAST node) {
215        DetailAST nextNode = node.getNextSibling();
216        if (nextNode == null) {
217            nextNode = node.getParent().getNextSibling();
218        }
219        return nextNode;
220    }
221
222    /**
223     * Returns the name of the given annotation.
224     *
225     * @param annotation annotation node.
226     * @return annotation name.
227     */
228    private static String getAnnotationName(DetailAST annotation) {
229        DetailAST identNode = annotation.findFirstToken(TokenTypes.IDENT);
230        if (identNode == null) {
231            identNode = annotation.findFirstToken(TokenTypes.DOT).getLastChild();
232        }
233        return identNode.getText();
234    }
235
236}