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