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> scopeStates = 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 scopeState = STATE_STATIC_VARIABLE_DEF;
084
085        /** The sub-state the check is in */
086        private Scope declarationAccess = Scope.PUBLIC;
087    }
088
089    /** If true, ignores the check to constructors. */
090    private boolean ignoreConstructors;
091    /** If true, ignore the check to methods. */
092    private boolean ignoreMethods;
093    /** If true, ignore the check to modifiers (fields, ...). */
094    private boolean ignoreModifiers;
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 ast)
109    {
110        final int parentType = ast.getParent().getType();
111        ScopeState state;
112
113        switch (ast.getType()) {
114            case TokenTypes.OBJBLOCK:
115                scopeStates.push(new ScopeState());
116                break;
117
118            case TokenTypes.CTOR_DEF:
119                if (parentType != TokenTypes.OBJBLOCK) {
120                    return;
121                }
122
123                state = scopeStates.peek();
124                if (state.scopeState > STATE_CTOR_DEF) {
125                    if (!ignoreConstructors) {
126                        log(ast, "declaration.order.constructor");
127                    }
128                }
129                else {
130                    state.scopeState = STATE_CTOR_DEF;
131                }
132                break;
133
134            case TokenTypes.METHOD_DEF:
135                state = scopeStates.peek();
136                if (parentType != TokenTypes.OBJBLOCK) {
137                    return;
138                }
139
140                if (state.scopeState > STATE_METHOD_DEF) {
141                    if (!ignoreMethods) {
142                        log(ast, "declaration.order.method");
143                    }
144                }
145                else {
146                    state.scopeState = STATE_METHOD_DEF;
147                }
148                break;
149
150            case TokenTypes.MODIFIERS:
151                if ((parentType != TokenTypes.VARIABLE_DEF)
152                    || (ast.getParent().getParent().getType()
153                        != TokenTypes.OBJBLOCK))
154                {
155                    return;
156                }
157
158                state = scopeStates.peek();
159                if (ast.findFirstToken(TokenTypes.LITERAL_STATIC) != null) {
160                    if (state.scopeState > STATE_STATIC_VARIABLE_DEF) {
161                        if (!ignoreModifiers
162                            || state.scopeState > STATE_INSTANCE_VARIABLE_DEF)
163                        {
164                            log(ast, "declaration.order.static");
165                        }
166                    }
167                    else {
168                        state.scopeState = STATE_STATIC_VARIABLE_DEF;
169                    }
170                }
171                else {
172                    if (state.scopeState > STATE_INSTANCE_VARIABLE_DEF) {
173                        log(ast, "declaration.order.instance");
174                    }
175                    else if (state.scopeState == STATE_STATIC_VARIABLE_DEF) {
176                        state.declarationAccess = Scope.PUBLIC;
177                        state.scopeState = STATE_INSTANCE_VARIABLE_DEF;
178                    }
179                }
180
181                final Scope access = ScopeUtils.getScopeFromMods(ast);
182                if (state.declarationAccess.compareTo(access) > 0) {
183                    if (!ignoreModifiers) {
184                        log(ast, "declaration.order.access");
185                    }
186                }
187                else {
188                    state.declarationAccess = access;
189                }
190                break;
191
192            default:
193        }
194    }
195
196    @Override
197    public void leaveToken(DetailAST ast)
198    {
199        switch (ast.getType()) {
200            case TokenTypes.OBJBLOCK:
201                scopeStates.pop();
202                break;
203
204            default:
205        }
206    }
207
208    /**
209     * Sets whether to ignore constructors.
210     * @param ignoreConstructors whether to ignore constructors.
211     */
212    public void setIgnoreConstructors(boolean ignoreConstructors)
213    {
214        this.ignoreConstructors = ignoreConstructors;
215    }
216
217    /**
218     * Sets whether to ignore methods.
219     * @param ignoreMethods whether to ignore methods.
220     */
221    public void setIgnoreMethods(boolean ignoreMethods)
222    {
223        this.ignoreMethods = ignoreMethods;
224    }
225
226    /**
227     * Sets whether to ignore modifiers.
228     * @param ignoreModifiers whether to ignore modifiers.
229     */
230    public void setIgnoreModifiers(boolean ignoreModifiers)
231    {
232        this.ignoreModifiers = ignoreModifiers;
233    }
234}