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 * @since 8.16 098 */ 099@StatelessCheck 100public class ClassMemberImpliedModifierCheck 101 extends AbstractCheck { 102 103 /** 104 * A key is pointing to the warning message text in "messages.properties" file. 105 */ 106 public static final String MSG_KEY = "class.implied.modifier"; 107 108 /** Name for 'static' keyword. */ 109 private static final String STATIC_KEYWORD = "static"; 110 111 /** 112 * Control whether to enforce that {@code static} is explicitly coded 113 * on nested enums in classes. 114 */ 115 private boolean violateImpliedStaticOnNestedEnum = true; 116 117 /** 118 * Control whether to enforce that {@code static} is explicitly coded 119 * on nested interfaces in classes. 120 */ 121 private boolean violateImpliedStaticOnNestedInterface = true; 122 123 /** 124 * Setter to control whether to enforce that {@code static} is explicitly coded 125 * on nested enums in classes. 126 * @param violateImplied 127 * True to perform the check, false to turn the check off. 128 */ 129 public void setViolateImpliedStaticOnNestedEnum(boolean violateImplied) { 130 violateImpliedStaticOnNestedEnum = violateImplied; 131 } 132 133 /** 134 * Setter to control whether to enforce that {@code static} is explicitly coded 135 * on nested interfaces in classes. 136 * @param violateImplied 137 * True to perform the check, false to turn the check off. 138 */ 139 public void setViolateImpliedStaticOnNestedInterface(boolean violateImplied) { 140 violateImpliedStaticOnNestedInterface = violateImplied; 141 } 142 143 @Override 144 public int[] getDefaultTokens() { 145 return getAcceptableTokens(); 146 } 147 148 @Override 149 public int[] getRequiredTokens() { 150 return getAcceptableTokens(); 151 } 152 153 @Override 154 public int[] getAcceptableTokens() { 155 return new int[] { 156 TokenTypes.INTERFACE_DEF, 157 TokenTypes.ENUM_DEF, 158 }; 159 } 160 161 @Override 162 public void visitToken(DetailAST ast) { 163 if (ScopeUtil.isInClassBlock(ast) || ScopeUtil.isInEnumBlock(ast)) { 164 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); 165 switch (ast.getType()) { 166 case TokenTypes.ENUM_DEF: 167 if (violateImpliedStaticOnNestedEnum 168 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 169 log(ast, MSG_KEY, STATIC_KEYWORD); 170 } 171 break; 172 case TokenTypes.INTERFACE_DEF: 173 if (violateImpliedStaticOnNestedInterface 174 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 175 log(ast, MSG_KEY, STATIC_KEYWORD); 176 } 177 break; 178 default: 179 throw new IllegalStateException(ast.toString()); 180 } 181 } 182 } 183 184}