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.modifier;
021
022import java.util.Iterator;
023import java.util.List;
024
025import com.google.common.collect.Lists;
026import com.puppycrawl.tools.checkstyle.api.Check;
027import com.puppycrawl.tools.checkstyle.api.DetailAST;
028import com.puppycrawl.tools.checkstyle.api.TokenTypes;
029
030/**
031 * <p>
032 * Checks that the order of modifiers conforms to the suggestions in the
033 * <a
034 * href="http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html">
035 * Java Language specification, sections 8.1.1, 8.3.1 and 8.4.3</a>.
036 * The correct order is:</p>
037
038<ol>
039  <li><span class="code">public</span></li>
040  <li><span class="code">protected</span></li>
041
042  <li><span class="code">private</span></li>
043  <li><span class="code">abstract</span></li>
044  <li><span class="code">static</span></li>
045  <li><span class="code">final</span></li>
046  <li><span class="code">transient</span></li>
047  <li><span class="code">volatile</span></li>
048
049  <li><span class="code">synchronized</span></li>
050  <li><span class="code">native</span></li>
051  <li><span class="code">strictfp</span></li>
052</ol>
053 * In additional, modifiers are checked to ensure all annotations
054 * are declared before all other modifiers.
055 * <p>
056 * Rationale: Code is easier to read if everybody follows
057 * a standard.
058 * </p>
059 * <p>
060 * An example of how to configure the check is:
061 * </p>
062 * <pre>
063 * &lt;module name="ModifierOrder"/&gt;
064 * </pre>
065 * @author Lars Kühne
066 */
067public class ModifierOrderCheck
068    extends Check {
069
070    /**
071     * A key is pointing to the warning message text in "messages.properties"
072     * file.
073     */
074    public static final String MSG_ANNOTATION_ORDER = "annotation.order";
075
076    /**
077     * A key is pointing to the warning message text in "messages.properties"
078     * file.
079     */
080    public static final String MSG_MODIFIER_ORDER = "mod.order";
081
082    /**
083     * The order of modifiers as suggested in sections 8.1.1,
084     * 8.3.1 and 8.4.3 of the JLS.
085     */
086    private static final String[] JLS_ORDER = {
087        "public", "protected", "private", "abstract", "static", "final",
088        "transient", "volatile", "synchronized", "native", "strictfp", "default",
089    };
090
091    @Override
092    public int[] getDefaultTokens() {
093        return getAcceptableTokens();
094    }
095
096    @Override
097    public int[] getAcceptableTokens() {
098        return new int[] {TokenTypes.MODIFIERS};
099    }
100
101    @Override
102    public int[] getRequiredTokens() {
103        return getAcceptableTokens();
104    }
105
106    @Override
107    public void visitToken(DetailAST ast) {
108        final List<DetailAST> mods = Lists.newArrayList();
109        DetailAST modifier = ast.getFirstChild();
110        while (modifier != null) {
111            mods.add(modifier);
112            modifier = modifier.getNextSibling();
113        }
114
115        if (!mods.isEmpty()) {
116            final DetailAST error = checkOrderSuggestedByJls(mods);
117            if (error != null) {
118                if (error.getType() == TokenTypes.ANNOTATION) {
119                    log(error.getLineNo(), error.getColumnNo(),
120                            MSG_ANNOTATION_ORDER,
121                             error.getFirstChild().getText()
122                             + error.getFirstChild().getNextSibling()
123                                .getText());
124                }
125                else {
126                    log(error.getLineNo(), error.getColumnNo(),
127                            MSG_MODIFIER_ORDER, error.getText());
128                }
129            }
130        }
131    }
132
133    /**
134     * Checks if the modifiers were added in the order suggested
135     * in the Java language specification.
136     *
137     * @param modifiers list of modifier AST tokens
138     * @return null if the order is correct, otherwise returns the offending
139     *     modifier AST.
140     */
141    private static DetailAST checkOrderSuggestedByJls(List<DetailAST> modifiers) {
142        final Iterator<DetailAST> iterator = modifiers.iterator();
143
144        //Speed past all initial annotations
145        DetailAST modifier = skipAnnotations(iterator);
146
147        DetailAST offendingModifier = null;
148
149        //All modifiers are annotations, no problem
150        if (modifier.getType() != TokenTypes.ANNOTATION) {
151            int index = 0;
152
153            while (modifier != null
154                    && offendingModifier == null) {
155
156                if (modifier.getType() == TokenTypes.ANNOTATION) {
157                    //Annotation not at start of modifiers, bad
158                    offendingModifier = modifier;
159                    break;
160                }
161
162                while (index < JLS_ORDER.length
163                       && !JLS_ORDER[index].equals(modifier.getText())) {
164                    index++;
165                }
166
167                if (index == JLS_ORDER.length) {
168                    //Current modifier is out of JLS order
169                    offendingModifier = modifier;
170                }
171                else if (iterator.hasNext()) {
172                    modifier = iterator.next();
173                }
174                else {
175                    //Reached end of modifiers without problem
176                    modifier = null;
177                }
178            }
179        }
180        return offendingModifier;
181    }
182
183    /**
184     * Skip all annotations in modifier block.
185     * @param modifierIterator iterator for collection of modifiers
186     * @return modifier next to last annotation
187     */
188    private static DetailAST skipAnnotations(Iterator<DetailAST> modifierIterator) {
189        DetailAST modifier;
190        do {
191            modifier = modifierIterator.next();
192        }
193        while (modifierIterator.hasNext() && modifier.getType() == TokenTypes.ANNOTATION);
194        return modifier;
195    }
196}