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