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}