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.coding;
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.Scope;
025import com.puppycrawl.tools.checkstyle.api.ScopeUtils;
026import com.puppycrawl.tools.checkstyle.api.TokenTypes;
027
028/**
029 * <p>
030 * Checks that the parts of a class or interface declaration
031 * appear in the order suggested by the
032 * <a
033 * href="http://java.sun.com/docs/codeconv/html/CodeConventions.doc2.html#1852"
034 * >Code Conventions for the Java Programming Language</a>.
035 *
036 *
037 * <ol>
038 * <li> Class (static) variables. First the public class variables, then
039 *      the protected, then package level (no access modifier), and then
040 *      the private. </li>
041 * <li> Instance variables. First the public class variables, then
042 *      the protected, then package level (no access modifier), and then
043 *      the private. </li>
044 * <li> Constructors </li>
045 * <li> Methods </li>
046 * </ol>
047 *
048 * <p>
049 * An example of how to configure the check is:
050 *
051 * <pre>
052 * &lt;module name="DeclarationOrder"/&gt;
053 * </pre>
054 *
055 * @author r_auckenthaler
056 */
057public class DeclarationOrderCheck extends Check
058{
059    /** State for the VARIABLE_DEF */
060    private static final int STATE_STATIC_VARIABLE_DEF = 1;
061
062    /** State for the VARIABLE_DEF */
063    private static final int STATE_INSTANCE_VARIABLE_DEF = 2;
064
065    /** State for the CTOR_DEF */
066    private static final int STATE_CTOR_DEF = 3;
067
068    /** State for the METHOD_DEF */
069    private static final int STATE_METHOD_DEF = 4;
070
071    /**
072     * List of Declaration States. This is necessary due to
073     * inner classes that have their own state
074     */
075    private final FastStack<ScopeState> mScopeStates = FastStack.newInstance();
076
077    /**
078     * private class to encapsulate the state
079     */
080    private static class ScopeState
081    {
082        /** The state the check is in */
083        private int mScopeState = STATE_STATIC_VARIABLE_DEF;
084
085        /** The sub-state the check is in */
086        private Scope mDeclarationAccess = Scope.PUBLIC;
087    }
088
089    /** If true, ignores the check to constructors. */
090    private boolean mIgnoreConstructors;
091    /** If true, ignore the check to methods. */
092    private boolean mIgnoreMethods;
093    /** If true, ignore the check to modifiers (fields, ...). */
094    private boolean mIgnoreModifiers;
095
096    @Override
097    public int[] getDefaultTokens()
098    {
099        return new int[] {
100            TokenTypes.CTOR_DEF,
101            TokenTypes.METHOD_DEF,
102            TokenTypes.MODIFIERS,
103            TokenTypes.OBJBLOCK,
104        };
105    }
106
107    @Override
108    public void visitToken(DetailAST aAST)
109    {
110        final int parentType = aAST.getParent().getType();
111        ScopeState state;
112
113        switch (aAST.getType()) {
114        case TokenTypes.OBJBLOCK:
115            mScopeStates.push(new ScopeState());
116            break;
117
118        case TokenTypes.CTOR_DEF:
119            if (parentType != TokenTypes.OBJBLOCK) {
120                return;
121            }
122
123            state = mScopeStates.peek();
124            if (state.mScopeState > STATE_CTOR_DEF) {
125                if (!mIgnoreConstructors) {
126                    log(aAST, "declaration.order.constructor");
127                }
128            }
129            else {
130                state.mScopeState = STATE_CTOR_DEF;
131            }
132            break;
133
134        case TokenTypes.METHOD_DEF:
135            state = mScopeStates.peek();
136            if (parentType != TokenTypes.OBJBLOCK) {
137                return;
138            }
139
140            if (state.mScopeState > STATE_METHOD_DEF) {
141                if (!mIgnoreMethods) {
142                    log(aAST, "declaration.order.method");
143                }
144            }
145            else {
146                state.mScopeState = STATE_METHOD_DEF;
147            }
148            break;
149
150        case TokenTypes.MODIFIERS:
151            if ((parentType != TokenTypes.VARIABLE_DEF)
152                || (aAST.getParent().getParent().getType()
153                    != TokenTypes.OBJBLOCK))
154            {
155                return;
156            }
157
158            state = mScopeStates.peek();
159            if (aAST.findFirstToken(TokenTypes.LITERAL_STATIC) != null) {
160                if (state.mScopeState > STATE_STATIC_VARIABLE_DEF) {
161                    if (!mIgnoreModifiers
162                        || state.mScopeState > STATE_INSTANCE_VARIABLE_DEF)
163                    {
164                        log(aAST, "declaration.order.static");
165                    }
166                }
167                else {
168                    state.mScopeState = STATE_STATIC_VARIABLE_DEF;
169                }
170            }
171            else {
172                if (state.mScopeState > STATE_INSTANCE_VARIABLE_DEF) {
173                    log(aAST, "declaration.order.instance");
174                }
175                else if (state.mScopeState == STATE_STATIC_VARIABLE_DEF) {
176                    state.mDeclarationAccess = Scope.PUBLIC;
177                    state.mScopeState = STATE_INSTANCE_VARIABLE_DEF;
178                }
179            }
180
181            final Scope access = ScopeUtils.getScopeFromMods(aAST);
182            if (state.mDeclarationAccess.compareTo(access) > 0) {
183                if (!mIgnoreModifiers) {
184                    log(aAST, "declaration.order.access");
185                }
186            }
187            else {
188                state.mDeclarationAccess = access;
189            }
190            break;
191
192        default:
193        }
194    }
195
196    @Override
197    public void leaveToken(DetailAST aAST)
198    {
199        switch (aAST.getType()) {
200        case TokenTypes.OBJBLOCK:
201            mScopeStates.pop();
202            break;
203
204        default:
205        }
206    }
207
208    /**
209     * Sets whether to ignore constructors.
210     * @param aIgnoreConstructors whether to ignore constructors.
211     */
212    public void setIgnoreConstructors(boolean aIgnoreConstructors)
213    {
214        mIgnoreConstructors = aIgnoreConstructors;
215    }
216
217    /**
218     * Sets whether to ignore methods.
219     * @param aIgnoreMethods whether to ignore methods.
220     */
221    public void setIgnoreMethods(boolean aIgnoreMethods)
222    {
223        mIgnoreMethods = aIgnoreMethods;
224    }
225
226    /**
227     * Sets whether to ignore modifiers.
228     * @param aIgnoreModifiers whether to ignore modifiers.
229     */
230    public void setIgnoreModifiers(boolean aIgnoreModifiers)
231    {
232        mIgnoreModifiers = aIgnoreModifiers;
233    }
234}