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 * <module name="AnnotationOnSameLine"/>
56 * </pre>
57 * <p>
58 * Example to allow annotations on the same line
59 * </p>
60 * <pre>
61 * @Override public int toString() { ... } // no violations
62 * @Before @Override public void set() { ... } // no violation
63 * </pre>
64 * <p>
65 * Example to disallow annotations on previous line
66 * </p>
67 * <pre>
68 * @SuppressWarnings("deprecation") // violation
69 * @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 }