View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2026 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.checkstyle;
6   
7   import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
8   import com.puppycrawl.tools.checkstyle.api.DetailAST;
9   import com.puppycrawl.tools.checkstyle.api.TokenTypes;
10  import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
11  import java.util.ArrayList;
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      /**
23       * Default constructor.
24       */
25      public ProtectedMethodInFinalClassCheck() {
26          // nothing to initialize
27      }
28  
29      @Override
30      public int[] getDefaultTokens() {
31          return new int[] {
32              TokenTypes.CLASS_DEF,
33          };
34      }
35  
36      @Override
37      public int[] getAcceptableTokens() {
38          return this.getDefaultTokens();
39      }
40  
41      @Override
42      public int[] getRequiredTokens() {
43          return this.getDefaultTokens();
44      }
45  
46      @Override
47      public void visitToken(final DetailAST ast) {
48          if (ast.getType() == TokenTypes.CLASS_DEF) {
49              final DetailAST modifiers = ast.findFirstToken(
50                  TokenTypes.MODIFIERS
51              );
52              if (modifiers.findFirstToken(TokenTypes.FINAL) != null) {
53                  this.checkMethods(ast);
54              }
55          }
56      }
57  
58      private void checkMethods(final DetailAST ast) {
59          final DetailAST objblock = ast.findFirstToken(TokenTypes.OBJBLOCK);
60          for (final DetailAST method
61              : ProtectedMethodInFinalClassCheck.findAllChildren(
62                  objblock, TokenTypes.METHOD_DEF
63              )
64          ) {
65              if (method
66                  .findFirstToken(TokenTypes.MODIFIERS)
67                  .findFirstToken(TokenTypes.LITERAL_PROTECTED) != null) {
68                  if (AnnotationUtil.containsAnnotation(method, "Override")) {
69                      this.log(
70                          method.getLineNo(),
71                          "Protected method is overriding default scoped method"
72                      );
73                  } else {
74                      this.log(
75                          method.getLineNo(),
76                          "Final class should not contain protected methods"
77                      );
78                  }
79              }
80          }
81      }
82  
83      private static Iterable<DetailAST> findAllChildren(final DetailAST base,
84          final int type) {
85          final List<DetailAST> children = new ArrayList<>(base.getChildCount());
86          DetailAST child = base.getFirstChild();
87          while (child != null) {
88              if (child.getType() == type) {
89                  children.add(child);
90              }
91              child = child.getNextSibling();
92          }
93          return children;
94      }
95  }