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 }