View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2025 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.checkstyle;
6   
7   import com.google.common.collect.Lists;
8   import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
9   import com.puppycrawl.tools.checkstyle.api.DetailAST;
10  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
11  import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
12  import java.util.List;
13  
14  /**
15   * Checks that final class doesn't contain protected methods unless they are
16   * overriding protected methods from superclass.
17   *
18   * @since 0.6
19   */
20  public final class ProtectedMethodInFinalClassCheck extends AbstractCheck {
21  
22      @Override
23      public int[] getDefaultTokens() {
24          return new int[] {
25              TokenTypes.CLASS_DEF,
26          };
27      }
28  
29      @Override
30      public int[] getAcceptableTokens() {
31          return this.getDefaultTokens();
32      }
33  
34      @Override
35      public int[] getRequiredTokens() {
36          return this.getDefaultTokens();
37      }
38  
39      @Override
40      public void visitToken(final DetailAST ast) {
41          if (ast.getType() == TokenTypes.CLASS_DEF) {
42              final DetailAST modifiers = ast.findFirstToken(
43                  TokenTypes.MODIFIERS
44              );
45              if (modifiers.findFirstToken(TokenTypes.FINAL) != null) {
46                  this.checkMethods(ast);
47              }
48          }
49      }
50  
51      /**
52       * Checks methods in current class have no protected modifier.
53       * @param ast DetailAST of CLASS_DEF
54       */
55      private void checkMethods(final DetailAST ast) {
56          final DetailAST objblock = ast.findFirstToken(TokenTypes.OBJBLOCK);
57          for (final DetailAST method
58              : ProtectedMethodInFinalClassCheck.findAllChildren(
59                  objblock, TokenTypes.METHOD_DEF
60              )
61          ) {
62              if (method
63                  .findFirstToken(TokenTypes.MODIFIERS)
64                  .findFirstToken(TokenTypes.LITERAL_PROTECTED) != null) {
65                  if (AnnotationUtil.containsAnnotation(method, "Override")) {
66                      this.log(
67                          method.getLineNo(),
68                          "Protected method is overriding default scoped method"
69                      );
70                  } else {
71                      this.log(
72                          method.getLineNo(),
73                          "Final class should not contain protected methods"
74                      );
75                  }
76              }
77          }
78      }
79  
80      /**
81       * Search for all children of given type.
82       * @param base Parent node to start from
83       * @param type Node type
84       * @return Iterable
85       */
86      private static Iterable<DetailAST> findAllChildren(final DetailAST base,
87          final int type) {
88          final List<DetailAST> children = Lists.newArrayList();
89          DetailAST child = base.getFirstChild();
90          while (child != null) {
91              if (child.getType() == type) {
92                  children.add(child);
93              }
94              child = child.getNextSibling();
95          }
96          return children;
97      }
98  }