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.FastStack; 024import com.puppycrawl.tools.checkstyle.api.ScopeUtils; 025import com.puppycrawl.tools.checkstyle.api.TokenTypes; 026 027/** 028 * <p> 029 * Checks that class which has only private ctors 030 * is declared as final. 031 * </p> 032 * <p> 033 * An example of how to configure the check is: 034 * </p> 035 * <pre> 036 * <module name="FinalClass"/> 037 * </pre> 038 * @author o_sukhodolsky 039 */ 040public class FinalClassCheck 041 extends Check 042{ 043 /** Keeps ClassDesc objects for stack of declared classes. */ 044 private final FastStack<ClassDesc> mClasses = FastStack.newInstance(); 045 046 @Override 047 public int[] getDefaultTokens() 048 { 049 return new int[]{TokenTypes.CLASS_DEF, TokenTypes.CTOR_DEF}; 050 } 051 052 @Override 053 public void visitToken(DetailAST aAST) 054 { 055 final DetailAST modifiers = aAST.findFirstToken(TokenTypes.MODIFIERS); 056 057 if (aAST.getType() == TokenTypes.CLASS_DEF) { 058 final boolean isFinal = (modifiers != null) 059 && modifiers.branchContains(TokenTypes.FINAL); 060 final boolean isAbstract = (modifiers != null) 061 && modifiers.branchContains(TokenTypes.ABSTRACT); 062 mClasses.push(new ClassDesc(isFinal, isAbstract)); 063 } 064 else if (!ScopeUtils.inEnumBlock(aAST)) { //ctors in enums don't matter 065 final ClassDesc desc = mClasses.peek(); 066 if ((modifiers != null) 067 && modifiers.branchContains(TokenTypes.LITERAL_PRIVATE)) 068 { 069 desc.reportPrivateCtor(); 070 } 071 else { 072 desc.reportNonPrivateCtor(); 073 } 074 } 075 } 076 077 @Override 078 public void leaveToken(DetailAST aAST) 079 { 080 if (aAST.getType() != TokenTypes.CLASS_DEF) { 081 return; 082 } 083 084 final ClassDesc desc = mClasses.pop(); 085 if (!desc.isDeclaredAsFinal() 086 && !desc.isDeclaredAsAbstract() 087 && desc.hasPrivateCtor() 088 && !desc.hasNonPrivateCtor()) 089 { 090 final String className = 091 aAST.findFirstToken(TokenTypes.IDENT).getText(); 092 log(aAST.getLineNo(), "final.class", className); 093 } 094 } 095 096 /** maintains information about class' ctors */ 097 private static final class ClassDesc 098 { 099 /** is class declared as final */ 100 private final boolean mDeclaredAsFinal; 101 102 /** is class declared as abstract */ 103 private final boolean mDeclaredAsAbstract; 104 105 /** does class have non-provate ctors */ 106 private boolean mHasNonPrivateCtor; 107 108 /** does class have private ctors */ 109 private boolean mHasPrivateCtor; 110 111 /** 112 * create a new ClassDesc instance. 113 * @param aDeclaredAsFinal indicates if the 114 * class declared as final 115 * @param aDeclaredAsAbstract indicates if the 116 * class declared as abstract 117 */ 118 ClassDesc(boolean aDeclaredAsFinal, boolean aDeclaredAsAbstract) 119 { 120 mDeclaredAsFinal = aDeclaredAsFinal; 121 mDeclaredAsAbstract = aDeclaredAsAbstract; 122 } 123 124 /** adds private ctor. */ 125 void reportPrivateCtor() 126 { 127 mHasPrivateCtor = true; 128 } 129 130 /** adds non-private ctor. */ 131 void reportNonPrivateCtor() 132 { 133 mHasNonPrivateCtor = true; 134 } 135 136 /** 137 * does class have private ctors. 138 * @return true if class has private ctors 139 */ 140 boolean hasPrivateCtor() 141 { 142 return mHasPrivateCtor; 143 } 144 145 /** 146 * does class have non-private ctors. 147 * @return true if class has non-private ctors 148 */ 149 boolean hasNonPrivateCtor() 150 { 151 return mHasNonPrivateCtor; 152 } 153 154 /** 155 * is class declared as final. 156 * @return true if class is declared as final 157 */ 158 boolean isDeclaredAsFinal() 159 { 160 return mDeclaredAsFinal; 161 } 162 163 /** 164 * is class declared as abstract. 165 * @return true if class is declared as final 166 */ 167 boolean isDeclaredAsAbstract() 168 { 169 return mDeclaredAsAbstract; 170 } 171 172 @Override 173 public String toString() 174 { 175 return this.getClass().getName() 176 + "[" 177 + "final=" + mDeclaredAsFinal 178 + " abstract=" + mDeclaredAsAbstract 179 + " pctor=" + mHasPrivateCtor 180 + " ctor=" + mHasNonPrivateCtor 181 + "]"; 182 } 183 } 184}