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 * &lt;module name="FinalClass"/&gt;
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}