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