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 * &lt;module name="ClassMemberImpliedModifier" /&gt;
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}