001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2015 the original author or authors.
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////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.coding;
021
022import java.util.ArrayDeque;
023import java.util.Deque;
024
025import com.puppycrawl.tools.checkstyle.api.Check;
026import com.puppycrawl.tools.checkstyle.api.DetailAST;
027import com.puppycrawl.tools.checkstyle.api.Scope;
028import com.puppycrawl.tools.checkstyle.api.TokenTypes;
029import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
030
031/**
032 * Checks that the parts of a class or interface declaration
033 * appear in the order suggested by the
034 * <a
035 * href="http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-141855.html#1852"
036 * >Code Conventions for the Java Programming Language</a>.
037 *
038 *
039 * <ol>
040 * <li> Class (static) variables. First the public class variables, then
041 *      the protected, then package level (no access modifier), and then
042 *      the private. </li>
043 * <li> Instance variables. First the public class variables, then
044 *      the protected, then package level (no access modifier), and then
045 *      the private. </li>
046 * <li> Constructors </li>
047 * <li> Methods </li>
048 * </ol>
049 *
050 * <p>Available options:
051 * <ul>
052 * <li>ignoreModifiers</li>
053 * <li>ignoreConstructors</li>
054 * </ul>
055 *
056 * <p>Purpose of <b>ignore*</b> option is to ignore related violations,
057 * however it still impacts on other class members.
058 *
059 * <p>For example:
060 * <pre>{@code
061 *     class K {
062 *         int a;
063 *         void m(){}
064 *         K(){}  &lt;-- "Constructor definition in wrong order"
065 *         int b; &lt;-- "Instance variable definition in wrong order"
066 *     }
067 * }</pre>
068 *
069 * <p>With <b>ignoreConstructors</b> option:
070 * <pre>{@code
071 *     class K {
072 *         int a;
073 *         void m(){}
074 *         K(){}
075 *         int b; &lt;-- "Instance variable definition in wrong order"
076 *     }
077 * }</pre>
078 *
079 * <p>With <b>ignoreConstructors</b> option and without a method definition in a source class:
080 * <pre>{@code
081 *     class K {
082 *         int a;
083 *         K(){}
084 *         int b; &lt;-- "Instance variable definition in wrong order"
085 *     }
086 * }</pre>
087 *
088 * <p>An example of how to configure the check is:
089 *
090 * <pre>
091 * &lt;module name="DeclarationOrder"/&gt;
092 * </pre>
093 *
094 * @author r_auckenthaler
095 */
096public class DeclarationOrderCheck extends Check {
097
098    /**
099     * A key is pointing to the warning message text in "messages.properties"
100     * file.
101     */
102    public static final String MSG_CONSTRUCTOR = "declaration.order.constructor";
103
104    /**
105     * A key is pointing to the warning message text in "messages.properties"
106     * file.
107     */
108    public static final String MSG_STATIC = "declaration.order.static";
109
110    /**
111     * A key is pointing to the warning message text in "messages.properties"
112     * file.
113     */
114    public static final String MSG_INSTANCE = "declaration.order.instance";
115
116    /**
117     * A key is pointing to the warning message text in "messages.properties"
118     * file.
119     */
120    public static final String MSG_ACCESS = "declaration.order.access";
121
122    /** State for the VARIABLE_DEF. */
123    private static final int STATE_STATIC_VARIABLE_DEF = 1;
124
125    /** State for the VARIABLE_DEF. */
126    private static final int STATE_INSTANCE_VARIABLE_DEF = 2;
127
128    /** State for the CTOR_DEF. */
129    private static final int STATE_CTOR_DEF = 3;
130
131    /** State for the METHOD_DEF. */
132    private static final int STATE_METHOD_DEF = 4;
133
134    /**
135     * List of Declaration States. This is necessary due to
136     * inner classes that have their own state
137     */
138    private final Deque<ScopeState> scopeStates = new ArrayDeque<>();
139
140    /** If true, ignores the check to constructors. */
141    private boolean ignoreConstructors;
142    /** If true, ignore the check to modifiers (fields, ...). */
143    private boolean ignoreModifiers;
144
145    @Override
146    public int[] getDefaultTokens() {
147        return getAcceptableTokens();
148    }
149
150    @Override
151    public int[] getAcceptableTokens() {
152        return new int[] {
153            TokenTypes.CTOR_DEF,
154            TokenTypes.METHOD_DEF,
155            TokenTypes.MODIFIERS,
156            TokenTypes.OBJBLOCK,
157        };
158    }
159
160    @Override
161    public int[] getRequiredTokens() {
162        return getAcceptableTokens();
163    }
164
165    @Override
166    public void visitToken(DetailAST ast) {
167        final int parentType = ast.getParent().getType();
168
169        switch (ast.getType()) {
170            case TokenTypes.OBJBLOCK:
171                scopeStates.push(new ScopeState());
172                break;
173            case TokenTypes.MODIFIERS:
174                if (parentType == TokenTypes.VARIABLE_DEF
175                    && ast.getParent().getParent().getType() == TokenTypes.OBJBLOCK) {
176                    processModifiers(ast);
177                }
178                break;
179            case TokenTypes.CTOR_DEF:
180                if (parentType == TokenTypes.OBJBLOCK) {
181                    processConstructor(ast);
182                }
183                break;
184            case TokenTypes.METHOD_DEF:
185                if (parentType == TokenTypes.OBJBLOCK) {
186                    final ScopeState state = scopeStates.peek();
187                    // nothing can be bigger than method's state
188                    state.currentScopeState = STATE_METHOD_DEF;
189                }
190                break;
191            default:
192                break;
193        }
194    }
195
196    /**
197     * Process constructor.
198     * @param ast constructor AST
199     */
200    private void processConstructor(DetailAST ast) {
201
202        final ScopeState state = scopeStates.peek();
203        if (state.currentScopeState > STATE_CTOR_DEF) {
204            if (!ignoreConstructors) {
205                log(ast, MSG_CONSTRUCTOR);
206            }
207        }
208        else {
209            state.currentScopeState = STATE_CTOR_DEF;
210        }
211    }
212
213    /**
214     * Process modifiers.
215     * @param ast ast of Modifiers
216     */
217    private void processModifiers(DetailAST ast) {
218
219        final ScopeState state = scopeStates.peek();
220        if (ast.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
221            if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
222                log(ast, MSG_INSTANCE);
223            }
224            else if (state.currentScopeState == STATE_STATIC_VARIABLE_DEF) {
225                state.declarationAccess = Scope.PUBLIC;
226                state.currentScopeState = STATE_INSTANCE_VARIABLE_DEF;
227            }
228        }
229        else {
230            if (state.currentScopeState > STATE_STATIC_VARIABLE_DEF) {
231                if (!ignoreModifiers
232                        || state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) {
233                    log(ast, MSG_STATIC);
234                }
235            }
236            else {
237                state.currentScopeState = STATE_STATIC_VARIABLE_DEF;
238            }
239        }
240
241        final Scope access = ScopeUtils.getScopeFromMods(ast);
242        if (state.declarationAccess.compareTo(access) > 0) {
243            if (!ignoreModifiers) {
244                log(ast, MSG_ACCESS);
245            }
246        }
247        else {
248            state.declarationAccess = access;
249        }
250    }
251
252    @Override
253    public void leaveToken(DetailAST ast) {
254        if (ast.getType() == TokenTypes.OBJBLOCK) {
255            scopeStates.pop();
256        }
257    }
258
259    /**
260     * Sets whether to ignore constructors.
261     * @param ignoreConstructors whether to ignore constructors.
262     */
263    public void setIgnoreConstructors(boolean ignoreConstructors) {
264        this.ignoreConstructors = ignoreConstructors;
265    }
266
267    /**
268     * Sets whether to ignore modifiers.
269     * @param ignoreModifiers whether to ignore modifiers.
270     */
271    public void setIgnoreModifiers(boolean ignoreModifiers) {
272        this.ignoreModifiers = ignoreModifiers;
273    }
274
275    /**
276     * Private class to encapsulate the state.
277     */
278    private static class ScopeState {
279        /** The state the check is in. */
280        private int currentScopeState = STATE_STATIC_VARIABLE_DEF;
281
282        /** The sub-state the check is in. */
283        private Scope declarationAccess = Scope.PUBLIC;
284    }
285}