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   * @since 0.6
18   */
19  public final class ProtectedMethodInFinalClassCheck extends AbstractCheck {
20  
21      @Override
22      public int[] getDefaultTokens() {
23          return new int[] {
24              TokenTypes.CLASS_DEF,
25          };
26      }
27  
28      @Override
29      public int[] getAcceptableTokens() {
30          return this.getDefaultTokens();
31      }
32  
33      @Override
34      public int[] getRequiredTokens() {
35          return this.getDefaultTokens();
36      }
37  
38      @Override
39      public void visitToken(final DetailAST ast) {
40          if (ast.getType() == TokenTypes.CLASS_DEF) {
41              final DetailAST modifiers = ast.findFirstToken(
42                  TokenTypes.MODIFIERS
43              );
44              if (modifiers.findFirstToken(TokenTypes.FINAL) != null) {
45                  this.checkMethods(ast);
46              }
47          }
48      }
49  
50      /**
51       * Checks methods in current class have no protected modifier.
52       * @param ast DetailAST of CLASS_DEF
53       */
54      private void checkMethods(final DetailAST ast) {
55          final DetailAST objblock = ast.findFirstToken(TokenTypes.OBJBLOCK);
56          for (final DetailAST method
57              : ProtectedMethodInFinalClassCheck.findAllChildren(
58                  objblock, TokenTypes.METHOD_DEF
59              )
60          ) {
61              if (method
62                  .findFirstToken(TokenTypes.MODIFIERS)
63                  .findFirstToken(TokenTypes.LITERAL_PROTECTED) != null) {
64                  if (AnnotationUtil.containsAnnotation(method, "Override")) {
65                      this.log(
66                          method.getLineNo(),
67                          "Protected method is overriding default scoped method"
68                      );
69                  } else {
70                      this.log(
71                          method.getLineNo(),
72                          "Final class should not contain protected methods"
73                      );
74                  }
75              }
76          }
77      }
78  
79      /**
80       * Search for all children of given type.
81       * @param base Parent node to start from
82       * @param type Node type
83       * @return Iterable
84       */
85      private static Iterable<DetailAST> findAllChildren(final DetailAST base,
86          final int type) {
87          final List<DetailAST> children = new ArrayList<>(base.getChildCount());
88          DetailAST child = base.getFirstChild();
89          while (child != null) {
90              if (child.getType() == type) {
91                  children.add(child);
92              }
93              child = child.getNextSibling();
94          }
95          return children;
96      }
97  }