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 * <module name="ModifierOrder"/> 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}