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