001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2017 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.ArrayList; 023import java.util.Iterator; 024import java.util.List; 025 026import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 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">default</span></li> 045 <li><span class="code">static</span></li> 046 <li><span class="code">final</span></li> 047 <li><span class="code">transient</span></li> 048 <li><span class="code">volatile</span></li> 049 050 <li><span class="code">synchronized</span></li> 051 <li><span class="code">native</span></li> 052 <li><span class="code">strictfp</span></li> 053</ol> 054 * In additional, modifiers are checked to ensure all annotations 055 * are declared before all other modifiers. 056 * <p> 057 * Rationale: Code is easier to read if everybody follows 058 * a standard. 059 * </p> 060 * <p> 061 * An example of how to configure the check is: 062 * </p> 063 * <pre> 064 * <module name="ModifierOrder"/> 065 * </pre> 066 * @author Lars Kühne 067 */ 068public class ModifierOrderCheck 069 extends AbstractCheck { 070 071 /** 072 * A key is pointing to the warning message text in "messages.properties" 073 * file. 074 */ 075 public static final String MSG_ANNOTATION_ORDER = "annotation.order"; 076 077 /** 078 * A key is pointing to the warning message text in "messages.properties" 079 * file. 080 */ 081 public static final String MSG_MODIFIER_ORDER = "mod.order"; 082 083 /** 084 * The order of modifiers as suggested in sections 8.1.1, 085 * 8.3.1 and 8.4.3 of the JLS. 086 */ 087 private static final String[] JLS_ORDER = { 088 "public", "protected", "private", "abstract", "default", "static", 089 "final", "transient", "volatile", "synchronized", "native", "strictfp", 090 }; 091 092 @Override 093 public int[] getDefaultTokens() { 094 return getAcceptableTokens(); 095 } 096 097 @Override 098 public int[] getAcceptableTokens() { 099 return new int[] {TokenTypes.MODIFIERS}; 100 } 101 102 @Override 103 public int[] getRequiredTokens() { 104 return getAcceptableTokens(); 105 } 106 107 @Override 108 public void visitToken(DetailAST ast) { 109 final List<DetailAST> mods = new ArrayList<>(); 110 DetailAST modifier = ast.getFirstChild(); 111 while (modifier != null) { 112 mods.add(modifier); 113 modifier = modifier.getNextSibling(); 114 } 115 116 if (!mods.isEmpty()) { 117 final DetailAST error = checkOrderSuggestedByJls(mods); 118 if (error != null) { 119 if (error.getType() == TokenTypes.ANNOTATION) { 120 log(error.getLineNo(), error.getColumnNo(), 121 MSG_ANNOTATION_ORDER, 122 error.getFirstChild().getText() 123 + error.getFirstChild().getNextSibling() 124 .getText()); 125 } 126 else { 127 log(error.getLineNo(), error.getColumnNo(), 128 MSG_MODIFIER_ORDER, error.getText()); 129 } 130 } 131 } 132 } 133 134 /** 135 * Checks if the modifiers were added in the order suggested 136 * in the Java language specification. 137 * 138 * @param modifiers list of modifier AST tokens 139 * @return null if the order is correct, otherwise returns the offending 140 * modifier AST. 141 */ 142 private static DetailAST checkOrderSuggestedByJls(List<DetailAST> modifiers) { 143 final Iterator<DetailAST> iterator = modifiers.iterator(); 144 145 //Speed past all initial annotations 146 DetailAST modifier = skipAnnotations(iterator); 147 148 DetailAST offendingModifier = null; 149 150 //All modifiers are annotations, no problem 151 if (modifier.getType() != TokenTypes.ANNOTATION) { 152 int index = 0; 153 154 while (modifier != null 155 && offendingModifier == null) { 156 157 if (modifier.getType() == TokenTypes.ANNOTATION) { 158 if (!isAnnotationOnType(modifier)) { 159 //Annotation not at start of modifiers, bad 160 offendingModifier = modifier; 161 } 162 break; 163 } 164 165 while (index < JLS_ORDER.length 166 && !JLS_ORDER[index].equals(modifier.getText())) { 167 index++; 168 } 169 170 if (index == JLS_ORDER.length) { 171 //Current modifier is out of JLS order 172 offendingModifier = modifier; 173 } 174 else if (iterator.hasNext()) { 175 modifier = iterator.next(); 176 } 177 else { 178 //Reached end of modifiers without problem 179 modifier = null; 180 } 181 } 182 } 183 return offendingModifier; 184 } 185 186 /** 187 * Skip all annotations in modifier block. 188 * @param modifierIterator iterator for collection of modifiers 189 * @return modifier next to last annotation 190 */ 191 private static DetailAST skipAnnotations(Iterator<DetailAST> modifierIterator) { 192 DetailAST modifier; 193 do { 194 modifier = modifierIterator.next(); 195 } while (modifierIterator.hasNext() && modifier.getType() == TokenTypes.ANNOTATION); 196 return modifier; 197 } 198 199 /** 200 * Checks whether annotation on type takes place. 201 * @param modifier modifier token. 202 * @return true if annotation on type takes place. 203 */ 204 private static boolean isAnnotationOnType(DetailAST modifier) { 205 boolean annotationOnType = false; 206 final DetailAST modifiers = modifier.getParent(); 207 final DetailAST definition = modifiers.getParent(); 208 final int definitionType = definition.getType(); 209 if (definitionType == TokenTypes.VARIABLE_DEF 210 || definitionType == TokenTypes.PARAMETER_DEF 211 || definitionType == TokenTypes.CTOR_DEF) { 212 annotationOnType = true; 213 } 214 else if (definitionType == TokenTypes.METHOD_DEF) { 215 final DetailAST typeToken = definition.findFirstToken(TokenTypes.TYPE); 216 final int methodReturnType = typeToken.getLastChild().getType(); 217 if (methodReturnType != TokenTypes.LITERAL_VOID) { 218 annotationOnType = true; 219 } 220 } 221 return annotationOnType; 222 } 223}