Clover Coverage Report - Checkstyle
Coverage timestamp: Fri May 9 2008 10:48:13 EST
../../../../../../img/srcFileCovDistChart8.png 75% of files have more coverage
34   158   20   11.33
20   75   0.59   3
3     6.67  
1    
 
  DesignForExtensionCheck       Line # 59 80.7% 0.80701756
22.87 34 20 20 0.59
 
  (1)
 
1    ////////////////////////////////////////////////////////////////////////////////
2    // checkstyle: Checks Java source code for adherence to a set of rules.
3    // Copyright (C) 2001-2005 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.DetailAST;
23    import com.puppycrawl.tools.checkstyle.api.Scope;
24    import com.puppycrawl.tools.checkstyle.api.ScopeUtils;
25    import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26   
27    /**
28    * Checks that classes are designed for inheritance.
29    *
30    * <p>
31    * More specifically, it enforces a programming style
32    * where superclasses provide empty "hooks" that can be
33    * implemented by subclasses.
34    * </p>
35    *
36    * <p>
37    * The exact rule is that nonprivate, nonstatic methods in
38    * nonfinal classes (or classes that do not
39    * only have private constructors) must either be
40    * <ul>
41    * <li>abstract or</li>
42    * <li>final or</li>
43    * <li>have an empty implementation</li>
44    * </ul>
45    * </p>
46    *
47    * <p>
48    * This protects superclasses against beeing broken by
49    * subclasses. The downside is that subclasses are limited
50    * in their flexibility, in particular they cannot prevent
51    * execution of code in the superclass, but that also
52    * means that subclasses can't forget to call their super
53    * method.
54    * </p>
55    *
56    * @author lkuehne
57    * @version $Revision: 1.12 $
58    */
 
59    public class DesignForExtensionCheck extends Check
60    {
61    /** {@inheritDoc} */
 
62  1 toggle public int[] getDefaultTokens()
63    {
64  1 return new int[] {TokenTypes.METHOD_DEF};
65    }
66   
67    /** {@inheritDoc} */
 
68  10 toggle public void visitToken(DetailAST aAST)
69    {
70    // nothing to do for Interfaces
71  10 if (ScopeUtils.inInterfaceOrAnnotationBlock(aAST)) {
72  1 return;
73    }
74   
75    // method is ok if it is private or abstract or final
76  9 final DetailAST modifiers = aAST.findFirstToken(TokenTypes.MODIFIERS);
77  9 if (modifiers.branchContains(TokenTypes.LITERAL_PRIVATE)
78    || modifiers.branchContains(TokenTypes.ABSTRACT)
79    || modifiers.branchContains(TokenTypes.FINAL)
80    || modifiers.branchContains(TokenTypes.LITERAL_STATIC))
81    {
82  2 return;
83    }
84   
85    // method is ok if containing class is not visible in API and
86    // cannot be extended by 3rd parties (bug #884035)
87  7 if (!ScopeUtils.getSurroundingScope(aAST).isIn(Scope.PROTECTED)) {
88  3 return;
89    }
90   
91    // method is ok if it is implementation can verified to be empty
92    // Note: native methods don't have impl in java code, so
93    // implementation can be null even if method not abstract
94  4 final DetailAST implementation = aAST.findFirstToken(TokenTypes.SLIST);
95  4 if ((implementation != null)
96    && (implementation.getFirstChild().getType() == TokenTypes.RCURLY))
97    {
98  2 return;
99    }
100   
101    // check if the containing class can be subclassed
102  2 final DetailAST classDef = findContainingClass(aAST);
103  2 final DetailAST classMods =
104    classDef.findFirstToken(TokenTypes.MODIFIERS);
105  2 if ((classDef.getType() == TokenTypes.ENUM_DEF)
106    || classMods.branchContains(TokenTypes.FINAL))
107    {
108  0 return;
109    }
110   
111    // check if subclassing is prevented by having only private ctors
112  2 final DetailAST objBlock = classDef.findFirstToken(TokenTypes.OBJBLOCK);
113   
114  2 boolean hasDefaultConstructor = true;
115  2 boolean hasExplNonPrivateCtor = false;
116   
117  2 DetailAST candidate = (DetailAST) objBlock.getFirstChild();
118   
119  24 while (candidate != null) {
120  22 if (candidate.getType() == TokenTypes.CTOR_DEF) {
121  0 hasDefaultConstructor = false;
122   
123  0 final DetailAST ctorMods =
124    candidate.findFirstToken(TokenTypes.MODIFIERS);
125  0 if (!ctorMods.branchContains(TokenTypes.LITERAL_PRIVATE)) {
126  0 hasExplNonPrivateCtor = true;
127  0 break;
128    }
129    }
130  22 candidate = (DetailAST) candidate.getNextSibling();
131    }
132   
133  2 if (hasDefaultConstructor || hasExplNonPrivateCtor) {
134  2 final String name = aAST.findFirstToken(TokenTypes.IDENT).getText();
135  2 log(aAST.getLineNo(), aAST.getColumnNo(),
136    "design.forExtension", name);
137    }
138   
139   
140   
141    }
142   
143    /**
144    * Searches the tree towards the root until it finds a CLASS_DEF node.
145    * @param aAST the start node for searching
146    * @return the CLASS_DEF node.
147    */
 
148  2 toggle private DetailAST findContainingClass(DetailAST aAST)
149    {
150  2 DetailAST searchAST = aAST;
151  6 while ((searchAST.getType() != TokenTypes.CLASS_DEF)
152    && (searchAST.getType() != TokenTypes.ENUM_DEF))
153    {
154  4 searchAST = searchAST.getParent();
155    }
156  2 return searchAST;
157    }
158    }