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   * <p>
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   * </p>
34   * <ul>
35   * <li>
36   * Property {@code tokens} - tokens to check
37   * Type is {@code java.lang.String[]}.
38   * Validation type is {@code tokenSet}.
39   * Default value is:
40   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
41   * CLASS_DEF</a>,
42   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
43   * INTERFACE_DEF</a>,
44   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
45   * ENUM_DEF</a>,
46   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
47   * METHOD_DEF</a>,
48   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
49   * CTOR_DEF</a>,
50   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
51   * VARIABLE_DEF</a>,
52   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF">
53   * RECORD_DEF</a>,
54   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMPACT_CTOR_DEF">
55   * COMPACT_CTOR_DEF</a>.
56   * </li>
57   * </ul>
58   * <p>
59   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
60   * </p>
61   * <p>
62   * Violation Message Keys:
63   * </p>
64   * <ul>
65   * <li>
66   * {@code annotation.same.line}
67   * </li>
68   * </ul>
69   *
70   * @since 8.2
71   */
72  @StatelessCheck
73  public class AnnotationOnSameLineCheck extends AbstractCheck {
74  
75      /** A key is pointing to the warning message text in "messages.properties" file. */
76      public static final String MSG_KEY_ANNOTATION_ON_SAME_LINE = "annotation.same.line";
77  
78      @Override
79      public int[] getDefaultTokens() {
80          return new int[] {
81              TokenTypes.CLASS_DEF,
82              TokenTypes.INTERFACE_DEF,
83              TokenTypes.ENUM_DEF,
84              TokenTypes.METHOD_DEF,
85              TokenTypes.CTOR_DEF,
86              TokenTypes.VARIABLE_DEF,
87              TokenTypes.RECORD_DEF,
88              TokenTypes.COMPACT_CTOR_DEF,
89          };
90      }
91  
92      @Override
93      public int[] getAcceptableTokens() {
94          return new int[] {
95              TokenTypes.CLASS_DEF,
96              TokenTypes.INTERFACE_DEF,
97              TokenTypes.ENUM_DEF,
98              TokenTypes.METHOD_DEF,
99              TokenTypes.CTOR_DEF,
100             TokenTypes.VARIABLE_DEF,
101             TokenTypes.PARAMETER_DEF,
102             TokenTypes.ANNOTATION_DEF,
103             TokenTypes.TYPECAST,
104             TokenTypes.LITERAL_THROWS,
105             TokenTypes.IMPLEMENTS_CLAUSE,
106             TokenTypes.TYPE_ARGUMENT,
107             TokenTypes.LITERAL_NEW,
108             TokenTypes.DOT,
109             TokenTypes.ANNOTATION_FIELD_DEF,
110             TokenTypes.RECORD_DEF,
111             TokenTypes.COMPACT_CTOR_DEF,
112         };
113     }
114 
115     @Override
116     public int[] getRequiredTokens() {
117         return CommonUtil.EMPTY_INT_ARRAY;
118     }
119 
120     @Override
121     public void visitToken(DetailAST ast) {
122         DetailAST nodeWithAnnotations = ast;
123         if (ast.getType() == TokenTypes.TYPECAST) {
124             nodeWithAnnotations = ast.findFirstToken(TokenTypes.TYPE);
125         }
126         DetailAST modifiersNode = nodeWithAnnotations.findFirstToken(TokenTypes.MODIFIERS);
127         if (modifiersNode == null) {
128             modifiersNode = nodeWithAnnotations.findFirstToken(TokenTypes.ANNOTATIONS);
129         }
130         if (modifiersNode != null) {
131             for (DetailAST annotationNode = modifiersNode.getFirstChild();
132                     annotationNode != null;
133                     annotationNode = annotationNode.getNextSibling()) {
134                 if (annotationNode.getType() == TokenTypes.ANNOTATION
135                         && !TokenUtil.areOnSameLine(annotationNode, getNextNode(annotationNode))) {
136                     log(annotationNode, MSG_KEY_ANNOTATION_ON_SAME_LINE,
137                           getAnnotationName(annotationNode));
138                 }
139             }
140         }
141     }
142 
143     /**
144      * Finds next node of ast tree.
145      *
146      * @param node current node
147      * @return node that is next to given
148      */
149     private static DetailAST getNextNode(DetailAST node) {
150         DetailAST nextNode = node.getNextSibling();
151         if (nextNode == null) {
152             nextNode = node.getParent().getNextSibling();
153         }
154         return nextNode;
155     }
156 
157     /**
158      * Returns the name of the given annotation.
159      *
160      * @param annotation annotation node.
161      * @return annotation name.
162      */
163     private static String getAnnotationName(DetailAST annotation) {
164         DetailAST identNode = annotation.findFirstToken(TokenTypes.IDENT);
165         if (identNode == null) {
166             identNode = annotation.findFirstToken(TokenTypes.DOT).getLastChild();
167         }
168         return identNode.getText();
169     }
170 
171 }