001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2018 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.utils; 021 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.FullIdent; 024import com.puppycrawl.tools.checkstyle.api.TokenTypes; 025 026/** 027 * Contains utility methods designed to work with annotations. 028 * 029 */ 030public final class AnnotationUtil { 031 032 /** 033 * Common message. 034 */ 035 private static final String THE_AST_IS_NULL = "the ast is null"; 036 037 /** 038 * Private utility constructor. 039 * @throws UnsupportedOperationException if called 040 */ 041 private AnnotationUtil() { 042 throw new UnsupportedOperationException("do not instantiate."); 043 } 044 045 /** 046 * Checks to see if the AST is annotated with 047 * the passed in annotation. 048 * 049 * <p> 050 * This method will not look for imports or package 051 * statements to detect the passed in annotation. 052 * </p> 053 * 054 * <p> 055 * To check if an AST contains a passed in annotation 056 * taking into account fully-qualified names 057 * (ex: java.lang.Override, Override) 058 * this method will need to be called twice. Once for each 059 * name given. 060 * </p> 061 * 062 * @param ast the current node 063 * @param annotation the annotation name to check for 064 * @return true if contains the annotation 065 */ 066 public static boolean containsAnnotation(final DetailAST ast, 067 String annotation) { 068 if (ast == null) { 069 throw new IllegalArgumentException(THE_AST_IS_NULL); 070 } 071 return getAnnotation(ast, annotation) != null; 072 } 073 074 /** 075 * Checks to see if the AST is annotated with 076 * any annotation. 077 * 078 * @param ast the current node 079 * @return true if contains an annotation 080 */ 081 public static boolean containsAnnotation(final DetailAST ast) { 082 if (ast == null) { 083 throw new IllegalArgumentException(THE_AST_IS_NULL); 084 } 085 final DetailAST holder = getAnnotationHolder(ast); 086 return holder != null && holder.findFirstToken(TokenTypes.ANNOTATION) != null; 087 } 088 089 /** 090 * Gets the AST that holds a series of annotations for the 091 * potentially annotated AST. Returns {@code null} 092 * the passed in AST is not have an Annotation Holder. 093 * 094 * @param ast the current node 095 * @return the Annotation Holder 096 */ 097 public static DetailAST getAnnotationHolder(DetailAST ast) { 098 if (ast == null) { 099 throw new IllegalArgumentException(THE_AST_IS_NULL); 100 } 101 102 final DetailAST annotationHolder; 103 104 if (ast.getType() == TokenTypes.ENUM_CONSTANT_DEF 105 || ast.getType() == TokenTypes.PACKAGE_DEF) { 106 annotationHolder = ast.findFirstToken(TokenTypes.ANNOTATIONS); 107 } 108 else { 109 annotationHolder = ast.findFirstToken(TokenTypes.MODIFIERS); 110 } 111 112 return annotationHolder; 113 } 114 115 /** 116 * Checks to see if the AST is annotated with 117 * the passed in annotation and return the AST 118 * representing that annotation. 119 * 120 * <p> 121 * This method will not look for imports or package 122 * statements to detect the passed in annotation. 123 * </p> 124 * 125 * <p> 126 * To check if an AST contains a passed in annotation 127 * taking into account fully-qualified names 128 * (ex: java.lang.Override, Override) 129 * this method will need to be called twice. Once for each 130 * name given. 131 * </p> 132 * 133 * @param ast the current node 134 * @param annotation the annotation name to check for 135 * @return the AST representing that annotation 136 */ 137 public static DetailAST getAnnotation(final DetailAST ast, 138 String annotation) { 139 if (ast == null) { 140 throw new IllegalArgumentException(THE_AST_IS_NULL); 141 } 142 143 if (annotation == null) { 144 throw new IllegalArgumentException("the annotation is null"); 145 } 146 147 if (CommonUtil.isBlank(annotation)) { 148 throw new IllegalArgumentException( 149 "the annotation is empty or spaces"); 150 } 151 152 final DetailAST holder = getAnnotationHolder(ast); 153 DetailAST result = null; 154 for (DetailAST child = holder.getFirstChild(); 155 child != null; child = child.getNextSibling()) { 156 if (child.getType() == TokenTypes.ANNOTATION) { 157 final DetailAST firstChild = child.findFirstToken(TokenTypes.AT); 158 final String name = 159 FullIdent.createFullIdent(firstChild.getNextSibling()).getText(); 160 if (annotation.equals(name)) { 161 result = child; 162 break; 163 } 164 } 165 } 166 167 return result; 168 } 169 170}