View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 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  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
28  
29  /**
30   * <div>
31   * Checks that annotations are located on the same line with their targets.
32   * Verifying with this check is not good practice, but it is using by some style guides.
33   * </div>
34   *
35   * <ul>
36   * <li>
37   * Property {@code tokens} - tokens to check
38   * Type is {@code java.lang.String[]}.
39   * Validation type is {@code tokenSet}.
40   * Default value is:
41   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
42   * CLASS_DEF</a>,
43   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
44   * INTERFACE_DEF</a>,
45   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
46   * ENUM_DEF</a>,
47   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
48   * METHOD_DEF</a>,
49   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
50   * CTOR_DEF</a>,
51   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
52   * VARIABLE_DEF</a>,
53   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF">
54   * RECORD_DEF</a>,
55   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMPACT_CTOR_DEF">
56   * COMPACT_CTOR_DEF</a>.
57   * </li>
58   * </ul>
59   *
60   * <p>
61   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
62   * </p>
63   *
64   * <p>
65   * Violation Message Keys:
66   * </p>
67   * <ul>
68   * <li>
69   * {@code annotation.same.line}
70   * </li>
71   * </ul>
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              TokenTypes.RECORD_DEF,
91              TokenTypes.COMPACT_CTOR_DEF,
92          };
93      }
94  
95      @Override
96      public int[] getAcceptableTokens() {
97          return new int[] {
98              TokenTypes.CLASS_DEF,
99              TokenTypes.INTERFACE_DEF,
100             TokenTypes.ENUM_DEF,
101             TokenTypes.METHOD_DEF,
102             TokenTypes.CTOR_DEF,
103             TokenTypes.VARIABLE_DEF,
104             TokenTypes.PARAMETER_DEF,
105             TokenTypes.ANNOTATION_DEF,
106             TokenTypes.TYPECAST,
107             TokenTypes.LITERAL_THROWS,
108             TokenTypes.IMPLEMENTS_CLAUSE,
109             TokenTypes.TYPE_ARGUMENT,
110             TokenTypes.LITERAL_NEW,
111             TokenTypes.DOT,
112             TokenTypes.ANNOTATION_FIELD_DEF,
113             TokenTypes.RECORD_DEF,
114             TokenTypes.COMPACT_CTOR_DEF,
115         };
116     }
117 
118     @Override
119     public int[] getRequiredTokens() {
120         return CommonUtil.EMPTY_INT_ARRAY;
121     }
122 
123     @Override
124     public void visitToken(DetailAST ast) {
125         DetailAST nodeWithAnnotations = ast;
126         if (ast.getType() == TokenTypes.TYPECAST) {
127             nodeWithAnnotations = ast.findFirstToken(TokenTypes.TYPE);
128         }
129         DetailAST modifiersNode = nodeWithAnnotations.findFirstToken(TokenTypes.MODIFIERS);
130         if (modifiersNode == null) {
131             modifiersNode = nodeWithAnnotations.findFirstToken(TokenTypes.ANNOTATIONS);
132         }
133         if (modifiersNode != null) {
134             for (DetailAST annotationNode = modifiersNode.getFirstChild();
135                     annotationNode != null;
136                     annotationNode = annotationNode.getNextSibling()) {
137                 if (annotationNode.getType() == TokenTypes.ANNOTATION
138                         && !TokenUtil.areOnSameLine(annotationNode, getNextNode(annotationNode))) {
139                     log(annotationNode, MSG_KEY_ANNOTATION_ON_SAME_LINE,
140                           getAnnotationName(annotationNode));
141                 }
142             }
143         }
144     }
145 
146     /**
147      * Finds next node of ast tree.
148      *
149      * @param node current node
150      * @return node that is next to given
151      */
152     private static DetailAST getNextNode(DetailAST node) {
153         DetailAST nextNode = node.getNextSibling();
154         if (nextNode == null) {
155             nextNode = node.getParent().getNextSibling();
156         }
157         return nextNode;
158     }
159 
160     /**
161      * Returns the name of the given annotation.
162      *
163      * @param annotation annotation node.
164      * @return annotation name.
165      */
166     private static String getAnnotationName(DetailAST annotation) {
167         DetailAST identNode = annotation.findFirstToken(TokenTypes.IDENT);
168         if (identNode == null) {
169             identNode = annotation.findFirstToken(TokenTypes.DOT).getLastChild();
170         }
171         return identNode.getText();
172     }
173 
174 }