Java tutorial
//////////////////////////////////////////////////////////////////////////////// // checkstyle: Checks Java source code for adherence to a set of rules. // Copyright (C) 2001-2011 Oliver Burn // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //////////////////////////////////////////////////////////////////////////////// package com.github.sevntu.checkstyle.checks.annotation; import com.google.common.base.Joiner; import com.google.common.collect.Sets; import com.puppycrawl.tools.checkstyle.api.Check; import com.puppycrawl.tools.checkstyle.api.DetailAST; import com.puppycrawl.tools.checkstyle.api.TokenTypes; import java.util.Set; import java.util.TreeSet; /** * <p> * Check that annotation is used with all required parameters. * </p> * <p> * Parameters:<br> * <b>annotationName</b> - The name of the target annotation where enforcement of parameter * should happen.<br> * <b>requiredParameters</b> - Set of parameter names that are required on the target * annotation. Names can be specified on any order in target annotation. * </p> * <p> * <b>Example 1.</b><br> * Configuration: * </p> * <pre> * <module name="RequiredParameterForAnnotation"> * <property name="annotationName" value="TheAnnotation"/> * <property name="requiredParameters" value="ThePropertyName1"/> * </module> * </pre> * <p> * Result: * </p> * <pre> * <code> * {@literal @}TheAnnotation() //Violation. ThePropertyName1 missing. * someMethod() {} * * {@literal @}TheAnnotation(ThePropertyName2=2) //Violation. ThePropertyName1 missing. * class SomeClass {} * * {@literal @}TheAnnotation(ThePropertyName1=1) //Correct. * class SomeClass {} * * {@literal @}TheAnnotation(ThePropertyName2=2, ThePropertyName3=3, ThePropertyName1=1) //Correct. * class SomeClass {} * </code> * </pre> <p> * <b>Example 2.</b><br> * Configuration: * </p> * <pre> * <module name="RequiredParameterForAnnotation"> * <property name="annotationName" value="TheAnnotation"/> * <property name="requiredParameters" value="ThePropertyName1,ThePropertyName2,ThePropertyName3"/> * </module> * </pre> * <p> * Result: * </p> * <pre> * <code> * {@literal @}TheAnnotation() //Violation. ThePropertyName1, ThePropertyName2, ThePropertyName3 missing. * someMethod() {} * * {@literal @}TheAnnotation(ThePropertyName2=2) //Violation. ThePropertyName1, ThePropertyName3 missing. * class SomeClass {} * * {@literal @}TheAnnotation(ThePropertyName3=3, ThePropertyName2=2, ThePropertyName1=1) //Correct. * class SomeClass {} * </code> * </pre> * * @author <a href="mailto:andrew.uljanenko@gmail.com">Andrew Uljanenko</a> */ public class RequiredParameterForAnnotationCheck extends Check { /** * Key for error message. */ public static final String MSG_KEY = "annotation.missing.required.parameter"; /** * The annotation name we are interested in. */ private String annotationName; /** * Parameters that should be in annotation. */ private Set<String> requiredParameters = new TreeSet<String>(); /** * The annotation name we are interested in. */ public void setAnnotationName(String annotationName) { this.annotationName = annotationName; } /** * The required list of parameters we have to use. */ public void setRequiredParameters(String[] requiredPropertiesParameter) { for (String item : requiredPropertiesParameter) { this.requiredParameters.add(item); } } @Override public int[] getDefaultTokens() { return new int[] { TokenTypes.ANNOTATION }; } @Override public int[] getRequiredTokens() { return new int[] { TokenTypes.ANNOTATION }; }; @Override public void visitToken(DetailAST annotationNode) { String annotationName = getAnnotationName(annotationNode); if (annotationName.equals(this.annotationName)) { Set<String> missingParameters = Sets.difference(requiredParameters, getAnnotationParameters(annotationNode)); if (!missingParameters.isEmpty()) { String missingParametersAsString = Joiner.on(", ").join(missingParameters); log(annotationNode, MSG_KEY, this.annotationName, missingParametersAsString); } } } /** * Returns full name of an annotation. * @param aAnnotation * @return name of an annotation. */ private static String getAnnotationName(DetailAST annotationNode) { DetailAST identNode = annotationNode.findFirstToken(TokenTypes.IDENT); String result; if (identNode != null) { result = identNode.getText(); } else { StringBuilder builder = new StringBuilder(); DetailAST separationDotNode = annotationNode.findFirstToken(TokenTypes.DOT); while (separationDotNode.getType() == TokenTypes.DOT) { builder.insert(0, '.').insert(1, separationDotNode.getLastChild().getText()); separationDotNode = separationDotNode.getFirstChild(); } builder.insert(0, separationDotNode.getText()); result = builder.toString(); } return result; } /** * Returns the name of annotations properties. * @param aAnnotation * @return name of annotation properties. */ private static Set<String> getAnnotationParameters(DetailAST annotationNode) { Set<String> annotationParameters = new TreeSet<String>(); DetailAST annotationChildNode = annotationNode.getFirstChild(); while (annotationChildNode != null) { if (annotationChildNode.getType() == TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) { annotationParameters.add(annotationChildNode.getFirstChild().getText()); } annotationChildNode = annotationChildNode.getNextSibling(); } return annotationParameters; } }