All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck Maven / Gradle / Ivy

Go to download

Checkstyle is a development tool to help programmers write Java code that adheres to a coding standard

There is a newer version: 10.18.1
Show newest version
////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2019 the original author or authors.
//
// 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.puppycrawl.tools.checkstyle.checks.annotation;

import java.util.Locale;

import com.puppycrawl.tools.checkstyle.StatelessCheck;
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;

/**
 * 

* This check controls the style with the usage of annotations. *

*

* Annotations have three element styles starting with the least verbose. *

*
    *
  • * {@code ElementStyle.COMPACT_NO_ARRAY} *
  • *
  • * {@code ElementStyle.COMPACT} *
  • *
  • * {@code ElementStyle.EXPANDED} *
  • *
*

* To not enforce an element style a {@code ElementStyle.IGNORE} type is provided. * The desired style can be set through the {@code elementStyle} property. *

*

* Using the {@code ElementStyle.EXPANDED} style is more verbose. * The expanded version is sometimes referred to as "named parameters" in other languages. *

*

* Using the {@code ElementStyle.COMPACT} style is less verbose. * This style can only be used when there is an element called 'value' which is either * the sole element or all other elements have default values. *

*

* Using the {@code ElementStyle.COMPACT_NO_ARRAY} style is less verbose. * It is similar to the {@code ElementStyle.COMPACT} style but single value arrays are flagged. * With annotations a single value array does not need to be placed in an array initializer. *

*

* The ending parenthesis are optional when using annotations with no elements. * To always require ending parenthesis use the {@code ClosingParens.ALWAYS} type. * To never have ending parenthesis use the {@code ClosingParens.NEVER} type. * To not enforce a closing parenthesis preference a {@code ClosingParens.IGNORE} type is provided. * Set this through the {@code closingParens} property. *

*

* Annotations also allow you to specify arrays of elements in a standard format. * As with normal arrays, a trailing comma is optional. * To always require a trailing comma use the {@code TrailingArrayComma.ALWAYS} type. * To never have a trailing comma use the {@code TrailingArrayComma.NEVER} type. * To not enforce a trailing array comma preference a {@code TrailingArrayComma.IGNORE} type * is provided. Set this through the {@code trailingArrayComma} property. *

*

* By default the {@code ElementStyle} is set to {@code COMPACT_NO_ARRAY}, * the {@code TrailingArrayComma} is set to {@code NEVER}, * and the {@code ClosingParens} is set to {@code NEVER}. *

*

* According to the JLS, it is legal to include a trailing comma * in arrays used in annotations but Sun's Java 5 & 6 compilers will not * compile with this syntax. This may in be a bug in Sun's compilers * since eclipse 3.4's built-in compiler does allow this syntax as * defined in the JLS. Note: this was tested with compilers included with * JDK versions 1.5.0.17 and 1.6.0.11 and the compiler included with eclipse 3.4.1. *

*

* See * Java Language specification, §9.7. *

*
    *
  • * Property {@code elementStyle} - Define the annotation element styles. * Default value is {@code compact_no_array}. *
  • *
  • * Property {@code closingParens} - Define the policy for ending parenthesis. * Default value is {@code never}. *
  • *
  • * Property {@code trailingArrayComma} - Define the policy for trailing comma in arrays. * Default value is {@code never}. *
  • *
*

* To configure the check: *

*
 * <module name="AnnotationUseStyle"/>
 * 
*

* To configure the check to enforce an {@code expanded} style, * with a trailing array comma set to {@code never} * and always including the closing parenthesis. *

*
 * <module name="AnnotationUseStyle">
 *   <property name="elementStyle" value="expanded"/>
 *   <property name="trailingArrayComma" value="never"/>
 *   <property name="closingParens" value="always"/>
 * </module>
 * 
* * @since 5.0 * */ @StatelessCheck public final class AnnotationUseStyleCheck extends AbstractCheck { /** * Defines the styles for defining elements in an annotation. */ public enum ElementStyle { /** * Expanded example * *
@SuppressWarnings(value={"unchecked","unused",})
. */ EXPANDED, /** * Compact example * *
@SuppressWarnings({"unchecked","unused",})
*
or
*
@SuppressWarnings("unchecked")
. */ COMPACT, /** * Compact example * *
@SuppressWarnings("unchecked")
. */ COMPACT_NO_ARRAY, /** * Mixed styles. */ IGNORE, } /** * Defines the two styles for defining * elements in an annotation. * */ public enum TrailingArrayComma { /** * With comma example * *
@SuppressWarnings(value={"unchecked","unused",})
. */ ALWAYS, /** * Without comma example * *
@SuppressWarnings(value={"unchecked","unused"})
. */ NEVER, /** * Mixed styles. */ IGNORE, } /** * Defines the two styles for defining * elements in an annotation. * */ public enum ClosingParens { /** * With parens example * *
@Deprecated()
. */ ALWAYS, /** * Without parens example * *
@Deprecated
. */ NEVER, /** * Mixed styles. */ IGNORE, } /** * A key is pointing to the warning message text in "messages.properties" * file. */ public static final String MSG_KEY_ANNOTATION_INCORRECT_STYLE = "annotation.incorrect.style"; /** * A key is pointing to the warning message text in "messages.properties" * file. */ public static final String MSG_KEY_ANNOTATION_PARENS_MISSING = "annotation.parens.missing"; /** * A key is pointing to the warning message text in "messages.properties" * file. */ public static final String MSG_KEY_ANNOTATION_PARENS_PRESENT = "annotation.parens.present"; /** * A key is pointing to the warning message text in "messages.properties" * file. */ public static final String MSG_KEY_ANNOTATION_TRAILING_COMMA_MISSING = "annotation.trailing.comma.missing"; /** * A key is pointing to the warning message text in "messages.properties" * file. */ public static final String MSG_KEY_ANNOTATION_TRAILING_COMMA_PRESENT = "annotation.trailing.comma.present"; /** * The element name used to receive special linguistic support * for annotation use. */ private static final String ANNOTATION_ELEMENT_SINGLE_NAME = "value"; /** * Define the annotation element styles. */ private ElementStyle elementStyle = ElementStyle.COMPACT_NO_ARRAY; //defaulting to NEVER because of the strange compiler behavior /** * Define the policy for trailing comma in arrays. */ private TrailingArrayComma trailingArrayComma = TrailingArrayComma.NEVER; /** * Define the policy for ending parenthesis. */ private ClosingParens closingParens = ClosingParens.NEVER; /** * Setter to define the annotation element styles. * * @param style string representation */ public void setElementStyle(final String style) { elementStyle = getOption(ElementStyle.class, style); } /** * Setter to define the policy for trailing comma in arrays. * * @param comma string representation */ public void setTrailingArrayComma(final String comma) { trailingArrayComma = getOption(TrailingArrayComma.class, comma); } /** * Setter to define the policy for ending parenthesis. * * @param parens string representation */ public void setClosingParens(final String parens) { closingParens = getOption(ClosingParens.class, parens); } /** * Retrieves an {@link Enum Enum} type from a @{link String String}. * @param the enum type * @param enumClass the enum class * @param value the string representing the enum * @return the enum type */ private static > T getOption(final Class enumClass, final String value) { try { return Enum.valueOf(enumClass, value.trim().toUpperCase(Locale.ENGLISH)); } catch (final IllegalArgumentException iae) { throw new IllegalArgumentException("unable to parse " + value, iae); } } @Override public int[] getDefaultTokens() { return getRequiredTokens(); } @Override public int[] getRequiredTokens() { return new int[] { TokenTypes.ANNOTATION, }; } @Override public int[] getAcceptableTokens() { return getRequiredTokens(); } @Override public void visitToken(final DetailAST ast) { checkStyleType(ast); checkCheckClosingParens(ast); checkTrailingComma(ast); } /** * Checks to see if the * {@link ElementStyle AnnotationElementStyle} * is correct. * * @param annotation the annotation token */ private void checkStyleType(final DetailAST annotation) { switch (elementStyle) { case COMPACT_NO_ARRAY: checkCompactNoArrayStyle(annotation); break; case COMPACT: checkCompactStyle(annotation); break; case EXPANDED: checkExpandedStyle(annotation); break; case IGNORE: default: break; } } /** * Checks for expanded style type violations. * * @param annotation the annotation token */ private void checkExpandedStyle(final DetailAST annotation) { final int valuePairCount = annotation.getChildCount(TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR); if (valuePairCount == 0 && hasArguments(annotation)) { log(annotation.getLineNo(), MSG_KEY_ANNOTATION_INCORRECT_STYLE, ElementStyle.EXPANDED); } } /** * Checks that annotation has arguments. * * @param annotation to check * @return true if annotation has arguments, false otherwise */ private static boolean hasArguments(DetailAST annotation) { final DetailAST firstToken = annotation.findFirstToken(TokenTypes.LPAREN); return firstToken != null && firstToken.getNextSibling().getType() != TokenTypes.RPAREN; } /** * Checks for compact style type violations. * * @param annotation the annotation token */ private void checkCompactStyle(final DetailAST annotation) { final int valuePairCount = annotation.getChildCount( TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR); final DetailAST valuePair = annotation.findFirstToken( TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR); if (valuePairCount == 1 && ANNOTATION_ELEMENT_SINGLE_NAME.equals( valuePair.getFirstChild().getText())) { log(annotation.getLineNo(), MSG_KEY_ANNOTATION_INCORRECT_STYLE, ElementStyle.COMPACT); } } /** * Checks for compact no array style type violations. * * @param annotation the annotation token */ private void checkCompactNoArrayStyle(final DetailAST annotation) { final DetailAST arrayInit = annotation.findFirstToken(TokenTypes.ANNOTATION_ARRAY_INIT); //in compact style with one value if (arrayInit != null && arrayInit.getChildCount(TokenTypes.EXPR) == 1) { log(annotation.getLineNo(), MSG_KEY_ANNOTATION_INCORRECT_STYLE, ElementStyle.COMPACT_NO_ARRAY); } //in expanded style with pairs else { DetailAST ast = annotation.getFirstChild(); while (ast != null) { final DetailAST nestedArrayInit = ast.findFirstToken(TokenTypes.ANNOTATION_ARRAY_INIT); if (nestedArrayInit != null && nestedArrayInit.getChildCount(TokenTypes.EXPR) == 1) { log(annotation.getLineNo(), MSG_KEY_ANNOTATION_INCORRECT_STYLE, ElementStyle.COMPACT_NO_ARRAY); } ast = ast.getNextSibling(); } } } /** * Checks to see if the trailing comma is present if required or * prohibited. * * @param annotation the annotation token */ private void checkTrailingComma(final DetailAST annotation) { if (trailingArrayComma != TrailingArrayComma.IGNORE) { DetailAST child = annotation.getFirstChild(); while (child != null) { DetailAST arrayInit = null; if (child.getType() == TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) { arrayInit = child.findFirstToken(TokenTypes.ANNOTATION_ARRAY_INIT); } else if (child.getType() == TokenTypes.ANNOTATION_ARRAY_INIT) { arrayInit = child; } if (arrayInit != null) { logCommaViolation(arrayInit); } child = child.getNextSibling(); } } } /** * Logs a trailing array comma violation if one exists. * * @param ast the array init * {@link TokenTypes#ANNOTATION_ARRAY_INIT ANNOTATION_ARRAY_INIT}. */ private void logCommaViolation(final DetailAST ast) { final DetailAST rCurly = ast.findFirstToken(TokenTypes.RCURLY); //comma can be null if array is empty final DetailAST comma = rCurly.getPreviousSibling(); if (trailingArrayComma == TrailingArrayComma.ALWAYS) { if (comma == null || comma.getType() != TokenTypes.COMMA) { log(rCurly, MSG_KEY_ANNOTATION_TRAILING_COMMA_MISSING); } } else if (comma != null && comma.getType() == TokenTypes.COMMA) { log(comma, MSG_KEY_ANNOTATION_TRAILING_COMMA_PRESENT); } } /** * Checks to see if the closing parenthesis are present if required or * prohibited. * * @param ast the annotation token */ private void checkCheckClosingParens(final DetailAST ast) { if (closingParens != ClosingParens.IGNORE) { final DetailAST paren = ast.getLastChild(); if (closingParens == ClosingParens.ALWAYS) { if (paren.getType() != TokenTypes.RPAREN) { log(ast.getLineNo(), MSG_KEY_ANNOTATION_PARENS_MISSING); } } else if (paren.getPreviousSibling().getType() == TokenTypes.LPAREN) { log(ast.getLineNo(), MSG_KEY_ANNOTATION_PARENS_PRESENT); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy