View Javadoc
1   /*
2    * Copyright (c) 2011-2024 Qulice.com
3    *
4    * All rights reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions
8    * are met: 1) Redistributions of source code must retain the above
9    * copyright notice, this list of conditions and the following
10   * disclaimer. 2) Redistributions in binary form must reproduce the above
11   * copyright notice, this list of conditions and the following
12   * disclaimer in the documentation and/or other materials provided
13   * with the distribution. 3) Neither the name of the Qulice.com nor
14   * the names of its contributors may be used to endorse or promote
15   * products derived from this software without specific prior written
16   * permission.
17   *
18   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
20   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29   * OF THE POSSIBILITY OF SUCH DAMAGE.
30   */
31  package com.qulice.checkstyle;
32  
33  import com.google.common.collect.Lists;
34  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
35  import com.puppycrawl.tools.checkstyle.api.DetailAST;
36  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
37  import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
38  import java.util.List;
39  
40  /**
41   * Checks that final class doesn't contain protected methods unless they are
42   * overriding protected methods from superclass.
43   *
44   * @since 0.6
45   */
46  public final class ProtectedMethodInFinalClassCheck extends AbstractCheck {
47  
48      @Override
49      public int[] getDefaultTokens() {
50          return new int[] {
51              TokenTypes.CLASS_DEF,
52          };
53      }
54  
55      @Override
56      public int[] getAcceptableTokens() {
57          return this.getDefaultTokens();
58      }
59  
60      @Override
61      public int[] getRequiredTokens() {
62          return this.getDefaultTokens();
63      }
64  
65      @Override
66      public void visitToken(final DetailAST ast) {
67          if (ast.getType() == TokenTypes.CLASS_DEF) {
68              final DetailAST modifiers = ast.findFirstToken(
69                  TokenTypes.MODIFIERS
70              );
71              if (modifiers.findFirstToken(TokenTypes.FINAL) != null) {
72                  this.checkMethods(ast);
73              }
74          }
75      }
76  
77      /**
78       * Checks methods in current class have no protected modifier.
79       * @param ast DetailAST of CLASS_DEF
80       */
81      private void checkMethods(final DetailAST ast) {
82          final DetailAST objblock = ast.findFirstToken(TokenTypes.OBJBLOCK);
83          for (final DetailAST method
84              : ProtectedMethodInFinalClassCheck.findAllChildren(
85                  objblock, TokenTypes.METHOD_DEF
86              )
87          ) {
88              if (method
89                  .findFirstToken(TokenTypes.MODIFIERS)
90                  .findFirstToken(TokenTypes.LITERAL_PROTECTED) != null) {
91                  if (AnnotationUtil.containsAnnotation(method, "Override")) {
92                      this.log(
93                          method.getLineNo(),
94                          "Protected method is overriding default scoped method"
95                      );
96                  } else {
97                      this.log(
98                          method.getLineNo(),
99                          "Final class should not contain protected methods"
100                     );
101                 }
102             }
103         }
104     }
105 
106     /**
107      * Search for all children of given type.
108      * @param base Parent node to start from
109      * @param type Node type
110      * @return Iterable
111      */
112     private static Iterable<DetailAST> findAllChildren(final DetailAST base,
113         final int type) {
114         final List<DetailAST> children = Lists.newArrayList();
115         DetailAST child = base.getFirstChild();
116         while (child != null) {
117             if (child.getType() == type) {
118                 children.add(child);
119             }
120             child = child.getNextSibling();
121         }
122         return children;
123     }
124 }