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> classes = 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 ast) 054 { 055 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); 056 057 if (ast.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 classes.push(new ClassDesc(isFinal, isAbstract)); 063 } 064 else if (!ScopeUtils.inEnumBlock(ast)) { //ctors in enums don't matter 065 final ClassDesc desc = classes.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 ast) 079 { 080 if (ast.getType() != TokenTypes.CLASS_DEF) { 081 return; 082 } 083 084 final ClassDesc desc = classes.pop(); 085 if (!desc.isDeclaredAsFinal() 086 && !desc.isDeclaredAsAbstract() 087 && desc.hasPrivateCtor() 088 && !desc.hasNonPrivateCtor()) 089 { 090 final String className = 091 ast.findFirstToken(TokenTypes.IDENT).getText(); 092 log(ast.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 declaredAsFinal; 101 102 /** is class declared as abstract */ 103 private final boolean declaredAsAbstract; 104 105 /** does class have non-provate ctors */ 106 private boolean hasNonPrivateCtor; 107 108 /** does class have private ctors */ 109 private boolean hasPrivateCtor; 110 111 /** 112 * create a new ClassDesc instance. 113 * @param declaredAsFinal indicates if the 114 * class declared as final 115 * @param declaredAsAbstract indicates if the 116 * class declared as abstract 117 */ 118 ClassDesc(boolean declaredAsFinal, boolean declaredAsAbstract) 119 { 120 this.declaredAsFinal = declaredAsFinal; 121 this.declaredAsAbstract = declaredAsAbstract; 122 } 123 124 /** adds private ctor. */ 125 void reportPrivateCtor() 126 { 127 hasPrivateCtor = true; 128 } 129 130 /** adds non-private ctor. */ 131 void reportNonPrivateCtor() 132 { 133 hasNonPrivateCtor = 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 hasPrivateCtor; 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 hasNonPrivateCtor; 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 declaredAsFinal; 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 declaredAsAbstract; 170 } 171 172 @Override 173 public String toString() 174 { 175 return this.getClass().getName() 176 + "[" 177 + "final=" + declaredAsFinal 178 + " abstract=" + declaredAsAbstract 179 + " pctor=" + hasPrivateCtor 180 + " ctor=" + hasNonPrivateCtor 181 + "]"; 182 } 183 } 184}