Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/puppycrawl/tools/checkstyle/checks/design/DesignForExtensionCheck.java


1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2003  Oliver Burn
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  package com.puppycrawl.tools.checkstyle.checks.design;
20  
21  import com.puppycrawl.tools.checkstyle.api.Check;
22  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
23  import com.puppycrawl.tools.checkstyle.api.DetailAST;
24  import com.puppycrawl.tools.checkstyle.api.ScopeUtils;
25  
26  /**
27   * Checks that classes are designed for inheritance.
28   *
29   * <p>
30   * More specifically, it enforces a programming style
31   * where superclasses provide empty "hooks" that can be
32   * implemented by subclasses.
33   * </p>
34   *
35   * <p>
36   * The exact rule is that nonprivate, nonstatic methods in
37   * nonfinal classes (or classes that do not
38   * only have private constructors) must either be
39   * <ul>
40   * <li>abstract or</li>
41   * <li>final or</li>
42   * <li>have an empty implementation</li>
43   * </ul>
44   * </p>
45   *
46   * <p>
47   * This protects superclasses against beeing broken by
48   * subclasses. The downside is that subclasses are limited
49   * in their flexibility, in particular they cannot prevent
50   * execution of code in the superclass, but that also
51   * means that subclasses can't forget to call their super
52   * method.
53   * </p>
54   *
55   * @author lkuehne
56   * @version $Revision: 1.2 $
57   */
58  public class DesignForExtensionCheck extends Check
59  {
60      /** @see Check */
61      public int[] getDefaultTokens()
62      {
63          return new int[] {TokenTypes.METHOD_DEF};
64      }
65  
66      /** @see Check */
67      public void visitToken(DetailAST aAST)
68      {
69          // nothing to do for Interfaces
70          if (ScopeUtils.inInterfaceBlock(aAST)) {
71              return;
72          }
73  
74          // method is ok if it is private or abstract or final
75          DetailAST modifiers = aAST.findFirstToken(TokenTypes.MODIFIERS);
76          if (modifiers.branchContains(TokenTypes.LITERAL_PRIVATE)
77              || modifiers.branchContains(TokenTypes.ABSTRACT)
78              || modifiers.branchContains(TokenTypes.FINAL)
79              || modifiers.branchContains(TokenTypes.LITERAL_STATIC))
80          {
81              return;
82          }
83  
84          // method is ok if it is empty
85          DetailAST implemetation = aAST.findFirstToken(TokenTypes.SLIST);
86          if (implemetation.getFirstChild().getType() == TokenTypes.RCURLY) {
87              return;
88          }
89  
90          // check if the containing class can be subclassed
91          DetailAST classDef = findContainingClass(aAST);
92          DetailAST classMods = classDef.findFirstToken(TokenTypes.MODIFIERS);
93          if (classMods.branchContains(TokenTypes.FINAL)) {
94              return;
95          }
96  
97          // check if subclassing is prevented by having only private ctors
98          DetailAST objBlock = classDef.findFirstToken(TokenTypes.OBJBLOCK);
99  
100         boolean hasDefaultConstructor = true;
101         boolean hasExplNonPrivateCtor = false;
102 
103         DetailAST candidate = (DetailAST) objBlock.getFirstChild();
104 
105         while (candidate != null) {
106             if (candidate.getType() == TokenTypes.CTOR_DEF) {
107                 hasDefaultConstructor = false;
108 
109                 DetailAST ctorMods =
110                     candidate.findFirstToken(TokenTypes.MODIFIERS);
111                 if (!ctorMods.branchContains(TokenTypes.LITERAL_PRIVATE)) {
112                     hasExplNonPrivateCtor = true;
113                     break;
114                 }
115             }
116             candidate = (DetailAST) candidate.getNextSibling();
117         }
118 
119         if (hasDefaultConstructor || hasExplNonPrivateCtor) {
120             String name = aAST.findFirstToken(TokenTypes.IDENT).getText();
121             log(aAST.getLineNo(), aAST.getColumnNo(),
122                 "design.forExtension", name);
123         }
124 
125 
126 
127     }
128 
129     /**
130      * Searches the tree towards the root until it finds a CLASS_DEF node.
131      * @param aAST the start node for searching
132      * @return the CLASS_DEF node.
133      */
134     private DetailAST findContainingClass(DetailAST aAST)
135     {
136         DetailAST searchAST = aAST;
137         while (searchAST.getType() != TokenTypes.CLASS_DEF) {
138             searchAST = searchAST.getParent();
139         }
140         return searchAST;
141     }
142 }