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.annotation;
21  
22  import com.puppycrawl.tools.checkstyle.StatelessCheck;
23  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24  import com.puppycrawl.tools.checkstyle.api.DetailAST;
25  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
27  
28  /**
29   * <p>
30   * The check does verifying that annotations are located on the same line with their targets.
31   * Verifying with this check is not good practice, but it is using by some style guides.
32   * </p>
33   * <ul>
34   * <li>
35   * Property {@code tokens} - tokens to check
36   * Default value is:
37   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
38   * CLASS_DEF</a>,
39   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
40   * INTERFACE_DEF</a>,
41   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
42   * ENUM_DEF</a>,
43   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
44   * METHOD_DEF</a>,
45   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
46   * CTOR_DEF</a>,
47   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
48   * VARIABLE_DEF</a>.
49   * </li>
50   * </ul>
51   * <p>
52   * To configure the check:
53   * </p>
54   * <pre>
55   * &lt;module name=&quot;AnnotationOnSameLine&quot;/&gt;
56   * </pre>
57   * <p>
58   * Example to allow annotations on the same line
59   * </p>
60   * <pre>
61   * &#64;Override public int toString() { ... } // no violations
62   * &#64;Before &#64;Override public void set() { ... } // no violation
63   * </pre>
64   * <p>
65   * Example to disallow annotations on previous line
66   * </p>
67   * <pre>
68   * &#64;SuppressWarnings("deprecation") // violation
69   * &#64;Override // violation
70   * public int foo() { ... }
71   * </pre>
72   *
73   * @since 8.2
74   */
75  @StatelessCheck
76  public class AnnotationOnSameLineCheck extends AbstractCheck {
77  
78      /** A key is pointing to the warning message text in "messages.properties" file. */
79      public static final String MSG_KEY_ANNOTATION_ON_SAME_LINE = "annotation.same.line";
80  
81      @Override
82      public int[] getDefaultTokens() {
83          return new int[] {
84              TokenTypes.CLASS_DEF,
85              TokenTypes.INTERFACE_DEF,
86              TokenTypes.ENUM_DEF,
87              TokenTypes.METHOD_DEF,
88              TokenTypes.CTOR_DEF,
89              TokenTypes.VARIABLE_DEF,
90          };
91      }
92  
93      @Override
94      public int[] getAcceptableTokens() {
95          return new int[] {
96              TokenTypes.CLASS_DEF,
97              TokenTypes.INTERFACE_DEF,
98              TokenTypes.ENUM_DEF,
99              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 }