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 * Default value is {@code true}. 060 * </li> 061 * <li> 062 * Property {@code violateImpliedStaticOnNestedInterface} - Control whether to enforce that 063 * {@code static} is explicitly coded on nested interfaces in classes. 064 * Default value is {@code true}. 065 * </li> 066 * </ul> 067 * <p> 068 * This example checks that all implicit modifiers on nested interfaces and enums are 069 * explicitly specified in classes. 070 * </p> 071 * <p> 072 * Configuration: 073 * </p> 074 * <pre> 075 * <module name="ClassMemberImpliedModifier" /> 076 * </pre> 077 * <p> 078 * Code: 079 * </p> 080 * <pre> 081 * public final class Person { 082 * static interface Address1 { // valid 083 * } 084 * 085 * interface Address2 { // violation 086 * } 087 * 088 * static enum Age1 { // valid 089 * CHILD, ADULT 090 * } 091 * 092 * enum Age2 { // violation 093 * CHILD, ADULT 094 * } 095 * } 096 * </pre> 097 * 098 * @since 8.16 099 */ 100@StatelessCheck 101public class ClassMemberImpliedModifierCheck 102 extends AbstractCheck { 103 104 /** 105 * A key is pointing to the warning message text in "messages.properties" file. 106 */ 107 public static final String MSG_KEY = "class.implied.modifier"; 108 109 /** Name for 'static' keyword. */ 110 private static final String STATIC_KEYWORD = "static"; 111 112 /** 113 * Control whether to enforce that {@code static} is explicitly coded 114 * on nested enums in classes. 115 */ 116 private boolean violateImpliedStaticOnNestedEnum = true; 117 118 /** 119 * Control whether to enforce that {@code static} is explicitly coded 120 * on nested interfaces in classes. 121 */ 122 private boolean violateImpliedStaticOnNestedInterface = true; 123 124 /** 125 * Setter to control whether to enforce that {@code static} is explicitly coded 126 * on nested enums in classes. 127 * 128 * @param violateImplied 129 * True to perform the check, false to turn the check off. 130 */ 131 public void setViolateImpliedStaticOnNestedEnum(boolean violateImplied) { 132 violateImpliedStaticOnNestedEnum = violateImplied; 133 } 134 135 /** 136 * Setter to control whether to enforce that {@code static} is explicitly coded 137 * on nested interfaces in classes. 138 * 139 * @param violateImplied 140 * True to perform the check, false to turn the check off. 141 */ 142 public void setViolateImpliedStaticOnNestedInterface(boolean violateImplied) { 143 violateImpliedStaticOnNestedInterface = violateImplied; 144 } 145 146 @Override 147 public int[] getDefaultTokens() { 148 return getAcceptableTokens(); 149 } 150 151 @Override 152 public int[] getRequiredTokens() { 153 return getAcceptableTokens(); 154 } 155 156 @Override 157 public int[] getAcceptableTokens() { 158 return new int[] { 159 TokenTypes.INTERFACE_DEF, 160 TokenTypes.ENUM_DEF, 161 }; 162 } 163 164 @Override 165 public void visitToken(DetailAST ast) { 166 if (ScopeUtil.isInClassBlock(ast) || ScopeUtil.isInEnumBlock(ast)) { 167 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); 168 switch (ast.getType()) { 169 case TokenTypes.ENUM_DEF: 170 if (violateImpliedStaticOnNestedEnum 171 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 172 log(ast, MSG_KEY, STATIC_KEYWORD); 173 } 174 break; 175 case TokenTypes.INTERFACE_DEF: 176 if (violateImpliedStaticOnNestedInterface 177 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 178 log(ast, MSG_KEY, STATIC_KEYWORD); 179 } 180 break; 181 default: 182 throw new IllegalStateException(ast.toString()); 183 } 184 } 185 } 186 187}