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