001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2024 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 interface members and nested types.
031 * </p>
032 * <p>
033 * This check is effectively the opposite of
034 * <a href="https://checkstyle.org/checks/modifier/redundantmodifier.html#RedundantModifier">
035 * RedundantModifier</a>.
036 * It checks the modifiers on interface members, ensuring that certain modifiers are explicitly
037 * specified even though they are actually redundant.
038 * </p>
039 * <p>
040 * Methods in interfaces are {@code public} by default, however from Java 9 they can also be
041 * {@code private}. This check provides the ability to enforce that {@code public} is explicitly
042 * coded and not implicitly added by the compiler.
043 * </p>
044 * <p>
045 * From Java 8, there are three types of methods in interfaces - static methods marked with
046 * {@code static}, default methods marked with {@code default} and abstract methods which do not
047 * have to be marked with anything. From Java 9, there are also private methods marked with
048 * {@code private}. This check provides the ability to enforce that {@code abstract} is
049 * explicitly coded and not implicitly added by the compiler.
050 * </p>
051 * <p>
052 * Fields in interfaces are always {@code public static final} and as such the compiler does not
053 * require these modifiers. This check provides the ability to enforce that these modifiers are
054 * explicitly coded and not implicitly added by the compiler.
055 * </p>
056 * <p>
057 * Nested types within an interface are always {@code public static} and as such the compiler
058 * does not require the {@code public static} modifiers. This check provides the ability to
059 * enforce that the {@code public} and {@code static} modifiers are explicitly coded and not
060 * implicitly added by the compiler.
061 * </p>
062 * <pre>
063 * public interface AddressFactory {
064 *   // check enforces code contains "public static final"
065 *   public static final String UNKNOWN = "Unknown";
066 *
067 *   String OTHER = "Other";  // violation
068 *
069 *   // check enforces code contains "public" or "private"
070 *   public static AddressFactory instance();
071 *
072 *   // check enforces code contains "public abstract"
073 *   public abstract Address createAddress(String addressLine, String city);
074 *
075 *   List&lt;Address&gt; findAddresses(String city);  // violation
076 *
077 *   // check enforces default methods are explicitly declared "public"
078 *   public default Address createAddress(String city) {
079 *     return createAddress(UNKNOWN, city);
080 *   }
081 *
082 *   default Address createOtherAddress() {  // violation
083 *     return createAddress(OTHER, OTHER);
084 *   }
085 * }
086 * </pre>
087 * <p>
088 * Rationale for this check: Methods, fields and nested types are treated differently
089 * depending on whether they are part of an interface or part of a class. For example, by
090 * default methods are package-scoped on classes, but public in interfaces. However, from
091 * Java 8 onwards, interfaces have changed to be much more like abstract classes.
092 * Interfaces now have static and instance methods with code. Developers should not have to
093 * remember which modifiers are required and which are implied. This check allows the simpler
094 * alternative approach to be adopted where the implied modifiers must always be coded explicitly.
095 * </p>
096 * <ul>
097 * <li>
098 * Property {@code violateImpliedAbstractMethod} - Control whether to enforce that {@code abstract}
099 * is explicitly coded on interface methods.
100 * Type is {@code boolean}.
101 * Default value is {@code true}.
102 * </li>
103 * <li>
104 * Property {@code violateImpliedFinalField} - Control whether to enforce that {@code final}
105 * is explicitly coded on interface fields.
106 * Type is {@code boolean}.
107 * Default value is {@code true}.
108 * </li>
109 * <li>
110 * Property {@code violateImpliedPublicField} - Control whether to enforce that {@code public}
111 * is explicitly coded on interface fields.
112 * Type is {@code boolean}.
113 * Default value is {@code true}.
114 * </li>
115 * <li>
116 * Property {@code violateImpliedPublicMethod} - Control whether to enforce that {@code public}
117 * is explicitly coded on interface methods.
118 * Type is {@code boolean}.
119 * Default value is {@code true}.
120 * </li>
121 * <li>
122 * Property {@code violateImpliedPublicNested} - Control whether to enforce that {@code public}
123 * is explicitly coded on interface nested types.
124 * Type is {@code boolean}.
125 * Default value is {@code true}.
126 * </li>
127 * <li>
128 * Property {@code violateImpliedStaticField} - Control whether to enforce that {@code static}
129 * is explicitly coded on interface fields.
130 * Type is {@code boolean}.
131 * Default value is {@code true}.
132 * </li>
133 * <li>
134 * Property {@code violateImpliedStaticNested} - Control whether to enforce that {@code static}
135 * is explicitly coded on interface nested types.
136 * Type is {@code boolean}.
137 * Default value is {@code true}.
138 * </li>
139 * </ul>
140 * <p>
141 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
142 * </p>
143 * <p>
144 * Violation Message Keys:
145 * </p>
146 * <ul>
147 * <li>
148 * {@code interface.implied.modifier}
149 * </li>
150 * </ul>
151 *
152 * @since 8.12
153 */
154@StatelessCheck
155public class InterfaceMemberImpliedModifierCheck
156    extends AbstractCheck {
157
158    /**
159     * A key is pointing to the warning message text in "messages.properties" file.
160     */
161    public static final String MSG_KEY = "interface.implied.modifier";
162
163    /** Name for 'public' access modifier. */
164    private static final String PUBLIC_ACCESS_MODIFIER = "public";
165
166    /** Name for 'abstract' keyword. */
167    private static final String ABSTRACT_KEYWORD = "abstract";
168
169    /** Name for 'static' keyword. */
170    private static final String STATIC_KEYWORD = "static";
171
172    /** Name for 'final' keyword. */
173    private static final String FINAL_KEYWORD = "final";
174
175    /**
176     * Control whether to enforce that {@code public} is explicitly coded
177     * on interface fields.
178     */
179    private boolean violateImpliedPublicField = true;
180
181    /**
182     * Control whether to enforce that {@code static} is explicitly coded
183     * on interface fields.
184     */
185    private boolean violateImpliedStaticField = true;
186
187    /**
188     * Control whether to enforce that {@code final} is explicitly coded
189     * on interface fields.
190     */
191    private boolean violateImpliedFinalField = true;
192
193    /**
194     * Control whether to enforce that {@code public} is explicitly coded
195     * on interface methods.
196     */
197    private boolean violateImpliedPublicMethod = true;
198
199    /**
200     * Control whether to enforce that {@code abstract} is explicitly coded
201     * on interface methods.
202     */
203    private boolean violateImpliedAbstractMethod = true;
204
205    /**
206     * Control whether to enforce that {@code public} is explicitly coded
207     * on interface nested types.
208     */
209    private boolean violateImpliedPublicNested = true;
210
211    /**
212     * Control whether to enforce that {@code static} is explicitly coded
213     * on interface nested types.
214     */
215    private boolean violateImpliedStaticNested = true;
216
217    /**
218     * Setter to control whether to enforce that {@code public} is explicitly coded
219     * on interface fields.
220     *
221     * @param violateImpliedPublicField
222     *        True to perform the check, false to turn the check off.
223     * @since 8.12
224     */
225    public void setViolateImpliedPublicField(boolean violateImpliedPublicField) {
226        this.violateImpliedPublicField = violateImpliedPublicField;
227    }
228
229    /**
230     * Setter to control whether to enforce that {@code static} is explicitly coded
231     * on interface fields.
232     *
233     * @param violateImpliedStaticField
234     *        True to perform the check, false to turn the check off.
235     * @since 8.12
236     */
237    public void setViolateImpliedStaticField(boolean violateImpliedStaticField) {
238        this.violateImpliedStaticField = violateImpliedStaticField;
239    }
240
241    /**
242     * Setter to control whether to enforce that {@code final} is explicitly coded
243     * on interface fields.
244     *
245     * @param violateImpliedFinalField
246     *        True to perform the check, false to turn the check off.
247     * @since 8.12
248     */
249    public void setViolateImpliedFinalField(boolean violateImpliedFinalField) {
250        this.violateImpliedFinalField = violateImpliedFinalField;
251    }
252
253    /**
254     * Setter to control whether to enforce that {@code public} is explicitly coded
255     * on interface methods.
256     *
257     * @param violateImpliedPublicMethod
258     *        True to perform the check, false to turn the check off.
259     * @since 8.12
260     */
261    public void setViolateImpliedPublicMethod(boolean violateImpliedPublicMethod) {
262        this.violateImpliedPublicMethod = violateImpliedPublicMethod;
263    }
264
265    /**
266     * Setter to control whether to enforce that {@code abstract} is explicitly coded
267     * on interface methods.
268     *
269     * @param violateImpliedAbstractMethod
270     *        True to perform the check, false to turn the check off.
271     * @since 8.12
272     */
273    public void setViolateImpliedAbstractMethod(boolean violateImpliedAbstractMethod) {
274        this.violateImpliedAbstractMethod = violateImpliedAbstractMethod;
275    }
276
277    /**
278     * Setter to control whether to enforce that {@code public} is explicitly coded
279     * on interface nested types.
280     *
281     * @param violateImpliedPublicNested
282     *        True to perform the check, false to turn the check off.
283     * @since 8.12
284     */
285    public void setViolateImpliedPublicNested(boolean violateImpliedPublicNested) {
286        this.violateImpliedPublicNested = violateImpliedPublicNested;
287    }
288
289    /**
290     * Setter to control whether to enforce that {@code static} is explicitly coded
291     * on interface nested types.
292     *
293     * @param violateImpliedStaticNested
294     *        True to perform the check, false to turn the check off.
295     * @since 8.12
296     */
297    public void setViolateImpliedStaticNested(boolean violateImpliedStaticNested) {
298        this.violateImpliedStaticNested = violateImpliedStaticNested;
299    }
300
301    @Override
302    public int[] getDefaultTokens() {
303        return getAcceptableTokens();
304    }
305
306    @Override
307    public int[] getRequiredTokens() {
308        return getAcceptableTokens();
309    }
310
311    @Override
312    public int[] getAcceptableTokens() {
313        return new int[] {
314            TokenTypes.METHOD_DEF,
315            TokenTypes.VARIABLE_DEF,
316            TokenTypes.INTERFACE_DEF,
317            TokenTypes.CLASS_DEF,
318            TokenTypes.ENUM_DEF,
319        };
320    }
321
322    @Override
323    public void visitToken(DetailAST ast) {
324        if (ScopeUtil.isInInterfaceBlock(ast) && !ScopeUtil.isInCodeBlock(ast)) {
325            switch (ast.getType()) {
326                case TokenTypes.METHOD_DEF:
327                    processMethod(ast);
328                    break;
329                case TokenTypes.VARIABLE_DEF:
330                    processField(ast);
331                    break;
332                case TokenTypes.CLASS_DEF:
333                case TokenTypes.INTERFACE_DEF:
334                case TokenTypes.ENUM_DEF:
335                    processNestedType(ast);
336                    break;
337                default:
338                    throw new IllegalStateException(ast.toString());
339            }
340        }
341    }
342
343    /**
344     * Check method in interface.
345     *
346     * @param ast the method AST
347     */
348    private void processMethod(DetailAST ast) {
349        final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
350        if (violateImpliedPublicMethod
351                && modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null
352                && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) {
353            log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER);
354        }
355        if (violateImpliedAbstractMethod
356                && modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null
357                && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null
358                && modifiers.findFirstToken(TokenTypes.LITERAL_DEFAULT) == null
359                && modifiers.findFirstToken(TokenTypes.ABSTRACT) == null) {
360            log(ast, MSG_KEY, ABSTRACT_KEYWORD);
361        }
362    }
363
364    /**
365     * Check field in interface.
366     *
367     * @param ast the field AST
368     */
369    private void processField(DetailAST ast) {
370        final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
371        if (violateImpliedPublicField
372                && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) {
373            log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER);
374        }
375        if (violateImpliedStaticField
376                && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
377            log(ast, MSG_KEY, STATIC_KEYWORD);
378        }
379        if (violateImpliedFinalField
380                && modifiers.findFirstToken(TokenTypes.FINAL) == null) {
381            log(ast, MSG_KEY, FINAL_KEYWORD);
382        }
383    }
384
385    /**
386     * Check nested types in interface.
387     *
388     * @param ast the nested type AST
389     */
390    private void processNestedType(DetailAST ast) {
391        final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
392        if (violateImpliedPublicNested
393                && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) {
394            log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER);
395        }
396        if (violateImpliedStaticNested
397                && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
398            log(ast, MSG_KEY, STATIC_KEYWORD);
399        }
400    }
401
402}