001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2019 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.checks.modifier; 021 022import com.puppycrawl.tools.checkstyle.StatelessCheck; 023import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.api.TokenTypes; 026import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 027 028/** 029 * <p> 030 * Checks for implicit modifiers on nested types in classes. 031 * </p> 032 * <p> 033 * This check is effectively the opposite of <a href="#RedundantModifier">RedundantModifier</a>. 034 * It checks the modifiers on nested types in classes, ensuring that certain modifiers are 035 * explicitly specified even though they are actually redundant. 036 * </p> 037 * <p> 038 * Nested enums and interfaces within a class are always {@code static} and as such the compiler 039 * does not require the {@code static} modifier. This check provides the ability to enforce that 040 * the {@code static} modifier is explicitly coded and not implicitly added by the compiler. 041 * </p> 042 * <pre> 043 * public final class Person { 044 * enum Age { // violation 045 * CHILD, ADULT 046 * } 047 * } 048 * </pre> 049 * <p> 050 * Rationale for this check: Nested enums and interfaces are treated differently from nested 051 * classes as they are only allowed to be {@code static}. Developers should not need to remember 052 * this rule, and this check provides the means to enforce that the modifier is coded explicitly. 053 * </p> 054 * <ul> 055 * <li> 056 * Property {@code violateImpliedStaticOnNestedEnum} - Control whether to enforce that 057 * {@code static} is explicitly coded on nested enums in classes. 058 * Default value is {@code true}. 059 * </li> 060 * <li> 061 * Property {@code violateImpliedStaticOnNestedInterface} - Control whether to enforce that 062 * {@code static} is explicitly coded on nested interfaces in classes. 063 * Default value is {@code true}. 064 * </li> 065 * </ul> 066 * <p> 067 * This example checks that all implicit modifiers on nested interfaces and enums are 068 * explicitly specified in classes. 069 * </p> 070 * <p> 071 * Configuration: 072 * </p> 073 * <pre> 074 * <module name="ClassMemberImpliedModifier" /> 075 * </pre> 076 * <p> 077 * Code: 078 * </p> 079 * <pre> 080 * public final class Person { 081 * static interface Address1 { // valid 082 * } 083 * 084 * interface Address2 { // violation 085 * } 086 * 087 * static enum Age1 { // valid 088 * CHILD, ADULT 089 * } 090 * 091 * enum Age2 { // violation 092 * CHILD, ADULT 093 * } 094 * } 095 * </pre> 096 * @since 8.16 097 */ 098@StatelessCheck 099public class ClassMemberImpliedModifierCheck 100 extends AbstractCheck { 101 102 /** 103 * A key is pointing to the warning message text in "messages.properties" file. 104 */ 105 public static final String MSG_KEY = "class.implied.modifier"; 106 107 /** Name for 'static' keyword. */ 108 private static final String STATIC_KEYWORD = "static"; 109 110 /** 111 * Control whether to enforce that {@code static} is explicitly coded 112 * on nested enums in classes. 113 */ 114 private boolean violateImpliedStaticOnNestedEnum = true; 115 116 /** 117 * Control whether to enforce that {@code static} is explicitly coded 118 * on nested interfaces in classes. 119 */ 120 private boolean violateImpliedStaticOnNestedInterface = true; 121 122 /** 123 * Setter to control whether to enforce that {@code static} is explicitly coded 124 * on nested enums in classes. 125 * @param violateImplied 126 * True to perform the check, false to turn the check off. 127 */ 128 public void setViolateImpliedStaticOnNestedEnum(boolean violateImplied) { 129 violateImpliedStaticOnNestedEnum = violateImplied; 130 } 131 132 /** 133 * Setter to control whether to enforce that {@code static} is explicitly coded 134 * on nested interfaces in classes. 135 * @param violateImplied 136 * True to perform the check, false to turn the check off. 137 */ 138 public void setViolateImpliedStaticOnNestedInterface(boolean violateImplied) { 139 violateImpliedStaticOnNestedInterface = violateImplied; 140 } 141 142 @Override 143 public int[] getDefaultTokens() { 144 return getAcceptableTokens(); 145 } 146 147 @Override 148 public int[] getRequiredTokens() { 149 return getAcceptableTokens(); 150 } 151 152 @Override 153 public int[] getAcceptableTokens() { 154 return new int[] { 155 TokenTypes.INTERFACE_DEF, 156 TokenTypes.ENUM_DEF, 157 }; 158 } 159 160 @Override 161 public void visitToken(DetailAST ast) { 162 if (ScopeUtil.isInClassBlock(ast) || ScopeUtil.isInEnumBlock(ast)) { 163 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); 164 switch (ast.getType()) { 165 case TokenTypes.ENUM_DEF: 166 if (violateImpliedStaticOnNestedEnum 167 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 168 log(ast, MSG_KEY, STATIC_KEYWORD); 169 } 170 break; 171 case TokenTypes.INTERFACE_DEF: 172 if (violateImpliedStaticOnNestedInterface 173 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 174 log(ast, MSG_KEY, STATIC_KEYWORD); 175 } 176 break; 177 default: 178 throw new IllegalStateException(ast.toString()); 179 } 180 } 181 } 182 183}