001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2014  Oliver Burn
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019package com.puppycrawl.tools.checkstyle.checks.design;
020
021import com.puppycrawl.tools.checkstyle.api.Check;
022import com.puppycrawl.tools.checkstyle.api.DetailAST;
023import com.puppycrawl.tools.checkstyle.api.Scope;
024import com.puppycrawl.tools.checkstyle.api.ScopeUtils;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026
027/**
028 * Checks that classes are designed for inheritance.
029 *
030 * <p>
031 * More specifically, it enforces a programming style
032 * where superclasses provide empty "hooks" that can be
033 * implemented by subclasses.
034 * </p>
035 *
036 * <p>
037 * The exact rule is that nonprivate, nonstatic methods in
038 * nonfinal classes (or classes that do not
039 * only have private constructors) must either be
040 * <ul>
041 * <li>abstract or</li>
042 * <li>final or</li>
043 * <li>have an empty implementation</li>
044 * </ul>
045 *
046 *
047 * <p>
048 * This protects superclasses against being broken by
049 * subclasses. The downside is that subclasses are limited
050 * in their flexibility, in particular they cannot prevent
051 * execution of code in the superclass, but that also
052 * means that subclasses can't forget to call their super
053 * method.
054 * </p>
055 *
056 * @author lkuehne
057 */
058public class DesignForExtensionCheck extends Check
059{
060    @Override
061    public int[] getDefaultTokens()
062    {
063        return new int[] {TokenTypes.METHOD_DEF};
064    }
065
066    @Override
067    public void visitToken(DetailAST ast)
068    {
069        // nothing to do for Interfaces
070        if (ScopeUtils.inInterfaceOrAnnotationBlock(ast)) {
071            return;
072        }
073
074        // method is ok if it is private or abstract or final
075        final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
076        if (modifiers.branchContains(TokenTypes.LITERAL_PRIVATE)
077            || modifiers.branchContains(TokenTypes.ABSTRACT)
078            || modifiers.branchContains(TokenTypes.FINAL)
079            || modifiers.branchContains(TokenTypes.LITERAL_STATIC))
080        {
081            return;
082        }
083
084        // method is ok if containing class is not visible in API and
085        // cannot be extended by 3rd parties (bug #884035)
086        if (!ScopeUtils.getSurroundingScope(ast).isIn(Scope.PROTECTED)) {
087            return;
088        }
089
090        // method is ok if it is implementation can verified to be empty
091        // Note: native methods don't have impl in java code, so
092        // implementation can be null even if method not abstract
093        final DetailAST implementation = ast.findFirstToken(TokenTypes.SLIST);
094        if ((implementation != null)
095            && (implementation.getFirstChild().getType() == TokenTypes.RCURLY))
096        {
097            return;
098        }
099
100        // check if the containing class can be subclassed
101        final DetailAST classDef = findContainingClass(ast);
102        final DetailAST classMods =
103            classDef.findFirstToken(TokenTypes.MODIFIERS);
104        if ((classDef.getType() == TokenTypes.ENUM_DEF)
105            || classMods.branchContains(TokenTypes.FINAL))
106        {
107            return;
108        }
109
110        // check if subclassing is prevented by having only private ctors
111        final DetailAST objBlock = classDef.findFirstToken(TokenTypes.OBJBLOCK);
112
113        boolean hasDefaultConstructor = true;
114        boolean hasExplNonPrivateCtor = false;
115
116        DetailAST candidate = objBlock.getFirstChild();
117
118        while (candidate != null) {
119            if (candidate.getType() == TokenTypes.CTOR_DEF) {
120                hasDefaultConstructor = false;
121
122                final DetailAST ctorMods =
123                    candidate.findFirstToken(TokenTypes.MODIFIERS);
124                if (!ctorMods.branchContains(TokenTypes.LITERAL_PRIVATE)) {
125                    hasExplNonPrivateCtor = true;
126                    break;
127                }
128            }
129            candidate = candidate.getNextSibling();
130        }
131
132        if (hasDefaultConstructor || hasExplNonPrivateCtor) {
133            final String name = ast.findFirstToken(TokenTypes.IDENT).getText();
134            log(ast.getLineNo(), ast.getColumnNo(),
135                "design.forExtension", name);
136        }
137
138
139
140    }
141
142    /**
143     * Searches the tree towards the root until it finds a CLASS_DEF node.
144     * @param ast the start node for searching
145     * @return the CLASS_DEF node.
146     */
147    private DetailAST findContainingClass(DetailAST ast)
148    {
149        DetailAST searchAST = ast;
150        while ((searchAST.getType() != TokenTypes.CLASS_DEF)
151               && (searchAST.getType() != TokenTypes.ENUM_DEF))
152        {
153            searchAST = searchAST.getParent();
154        }
155        return searchAST;
156    }
157}