001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 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 * <div> 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 * </div> 034 * 035 * <ul> 036 * <li> 037 * Property {@code tokens} - tokens to check 038 * Type is {@code java.lang.String[]}. 039 * Validation type is {@code tokenSet}. 040 * Default value is: 041 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF"> 042 * CLASS_DEF</a>, 043 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF"> 044 * INTERFACE_DEF</a>, 045 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF"> 046 * ENUM_DEF</a>, 047 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 048 * METHOD_DEF</a>, 049 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF"> 050 * CTOR_DEF</a>, 051 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF"> 052 * VARIABLE_DEF</a>, 053 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF"> 054 * RECORD_DEF</a>, 055 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMPACT_CTOR_DEF"> 056 * COMPACT_CTOR_DEF</a>. 057 * </li> 058 * </ul> 059 * 060 * <p> 061 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 062 * </p> 063 * 064 * <p> 065 * Violation Message Keys: 066 * </p> 067 * <ul> 068 * <li> 069 * {@code annotation.same.line} 070 * </li> 071 * </ul> 072 * 073 * @since 8.2 074 */ 075@StatelessCheck 076public class AnnotationOnSameLineCheck extends AbstractCheck { 077 078 /** A key is pointing to the warning message text in "messages.properties" file. */ 079 public static final String MSG_KEY_ANNOTATION_ON_SAME_LINE = "annotation.same.line"; 080 081 @Override 082 public int[] getDefaultTokens() { 083 return new int[] { 084 TokenTypes.CLASS_DEF, 085 TokenTypes.INTERFACE_DEF, 086 TokenTypes.ENUM_DEF, 087 TokenTypes.METHOD_DEF, 088 TokenTypes.CTOR_DEF, 089 TokenTypes.VARIABLE_DEF, 090 TokenTypes.RECORD_DEF, 091 TokenTypes.COMPACT_CTOR_DEF, 092 }; 093 } 094 095 @Override 096 public int[] getAcceptableTokens() { 097 return new int[] { 098 TokenTypes.CLASS_DEF, 099 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}