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