Coverage Report - com.puppycrawl.tools.checkstyle.checks.modifier.ModifierOrderCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
ModifierOrderCheck
100%
54/54
100%
40/40
3.857
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2017 the original author or authors.
 4  
 //
 5  
 // This library is free software; you can redistribute it and/or
 6  
 // modify it under the terms of the GNU Lesser General Public
 7  
 // License as published by the Free Software Foundation; either
 8  
 // version 2.1 of the License, or (at your option) any later version.
 9  
 //
 10  
 // This library is distributed in the hope that it will be useful,
 11  
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 // Lesser General Public License for more details.
 14  
 //
 15  
 // You should have received a copy of the GNU Lesser General Public
 16  
 // License along with this library; if not, write to the Free Software
 17  
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 18  
 ////////////////////////////////////////////////////////////////////////////////
 19  
 
 20  
 package com.puppycrawl.tools.checkstyle.checks.modifier;
 21  
 
 22  
 import java.util.ArrayList;
 23  
 import java.util.Iterator;
 24  
 import java.util.List;
 25  
 
 26  
 import com.puppycrawl.tools.checkstyle.StatelessCheck;
 27  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 28  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 29  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 30  
 
 31  
 /**
 32  
  * <p>
 33  
  * Checks that the order of modifiers conforms to the suggestions in the
 34  
  * <a
 35  
  * href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html">
 36  
  * Java Language specification, sections 8.1.1, 8.3.1 and 8.4.3</a>.
 37  
  * The correct order is:</p>
 38  
 
 39  
 <ol>
 40  
   <li><span class="code">public</span></li>
 41  
   <li><span class="code">protected</span></li>
 42  
 
 43  
   <li><span class="code">private</span></li>
 44  
   <li><span class="code">abstract</span></li>
 45  
   <li><span class="code">default</span></li>
 46  
   <li><span class="code">static</span></li>
 47  
   <li><span class="code">final</span></li>
 48  
   <li><span class="code">transient</span></li>
 49  
   <li><span class="code">volatile</span></li>
 50  
 
 51  
   <li><span class="code">synchronized</span></li>
 52  
   <li><span class="code">native</span></li>
 53  
   <li><span class="code">strictfp</span></li>
 54  
 </ol>
 55  
  * In additional, modifiers are checked to ensure all annotations
 56  
  * are declared before all other modifiers.
 57  
  * <p>
 58  
  * Rationale: Code is easier to read if everybody follows
 59  
  * a standard.
 60  
  * </p>
 61  
  * <p>
 62  
  * An example of how to configure the check is:
 63  
  * </p>
 64  
  * <pre>
 65  
  * &lt;module name="ModifierOrder"/&gt;
 66  
  * </pre>
 67  
  * @author Lars K├╝hne
 68  
  */
 69  
 @StatelessCheck
 70  16
 public class ModifierOrderCheck
 71  
     extends AbstractCheck {
 72  
 
 73  
     /**
 74  
      * A key is pointing to the warning message text in "messages.properties"
 75  
      * file.
 76  
      */
 77  
     public static final String MSG_ANNOTATION_ORDER = "annotation.order";
 78  
 
 79  
     /**
 80  
      * A key is pointing to the warning message text in "messages.properties"
 81  
      * file.
 82  
      */
 83  
     public static final String MSG_MODIFIER_ORDER = "mod.order";
 84  
 
 85  
     /**
 86  
      * The order of modifiers as suggested in sections 8.1.1,
 87  
      * 8.3.1 and 8.4.3 of the JLS.
 88  
      */
 89  2
     private static final String[] JLS_ORDER = {
 90  
         "public", "protected", "private", "abstract", "default", "static",
 91  
         "final", "transient", "volatile", "synchronized", "native", "strictfp",
 92  
     };
 93  
 
 94  
     @Override
 95  
     public int[] getDefaultTokens() {
 96  20
         return getRequiredTokens();
 97  
     }
 98  
 
 99  
     @Override
 100  
     public int[] getAcceptableTokens() {
 101  6
         return getRequiredTokens();
 102  
     }
 103  
 
 104  
     @Override
 105  
     public int[] getRequiredTokens() {
 106  46
         return new int[] {TokenTypes.MODIFIERS};
 107  
     }
 108  
 
 109  
     @Override
 110  
     public void visitToken(DetailAST ast) {
 111  93
         final List<DetailAST> mods = new ArrayList<>();
 112  93
         DetailAST modifier = ast.getFirstChild();
 113  191
         while (modifier != null) {
 114  98
             mods.add(modifier);
 115  98
             modifier = modifier.getNextSibling();
 116  
         }
 117  
 
 118  93
         if (!mods.isEmpty()) {
 119  57
             final DetailAST error = checkOrderSuggestedByJls(mods);
 120  57
             if (error != null) {
 121  9
                 if (error.getType() == TokenTypes.ANNOTATION) {
 122  10
                     log(error.getLineNo(), error.getColumnNo(),
 123  
                             MSG_ANNOTATION_ORDER,
 124  5
                              error.getFirstChild().getText()
 125  5
                              + error.getFirstChild().getNextSibling()
 126  5
                                 .getText());
 127  
                 }
 128  
                 else {
 129  8
                     log(error.getLineNo(), error.getColumnNo(),
 130  4
                             MSG_MODIFIER_ORDER, error.getText());
 131  
                 }
 132  
             }
 133  
         }
 134  93
     }
 135  
 
 136  
     /**
 137  
      * Checks if the modifiers were added in the order suggested
 138  
      * in the Java language specification.
 139  
      *
 140  
      * @param modifiers list of modifier AST tokens
 141  
      * @return null if the order is correct, otherwise returns the offending
 142  
      *     modifier AST.
 143  
      */
 144  
     private static DetailAST checkOrderSuggestedByJls(List<DetailAST> modifiers) {
 145  57
         final Iterator<DetailAST> iterator = modifiers.iterator();
 146  
 
 147  
         //Speed past all initial annotations
 148  57
         DetailAST modifier = skipAnnotations(iterator);
 149  
 
 150  57
         DetailAST offendingModifier = null;
 151  
 
 152  
         //All modifiers are annotations, no problem
 153  57
         if (modifier.getType() != TokenTypes.ANNOTATION) {
 154  53
             int index = 0;
 155  
 
 156  118
             while (modifier != null
 157  
                     && offendingModifier == null) {
 158  
 
 159  78
                 if (modifier.getType() == TokenTypes.ANNOTATION) {
 160  13
                     if (!isAnnotationOnType(modifier)) {
 161  
                         //Annotation not at start of modifiers, bad
 162  5
                         offendingModifier = modifier;
 163  
                     }
 164  
                     break;
 165  
                 }
 166  
 
 167  233
                 while (index < JLS_ORDER.length
 168  229
                        && !JLS_ORDER[index].equals(modifier.getText())) {
 169  168
                     index++;
 170  
                 }
 171  
 
 172  65
                 if (index == JLS_ORDER.length) {
 173  
                     //Current modifier is out of JLS order
 174  4
                     offendingModifier = modifier;
 175  
                 }
 176  61
                 else if (iterator.hasNext()) {
 177  25
                     modifier = iterator.next();
 178  
                 }
 179  
                 else {
 180  
                     //Reached end of modifiers without problem
 181  36
                     modifier = null;
 182  
                 }
 183  
             }
 184  
         }
 185  57
         return offendingModifier;
 186  
     }
 187  
 
 188  
     /**
 189  
      * Skip all annotations in modifier block.
 190  
      * @param modifierIterator iterator for collection of modifiers
 191  
      * @return modifier next to last annotation
 192  
      */
 193  
     private static DetailAST skipAnnotations(Iterator<DetailAST> modifierIterator) {
 194  
         DetailAST modifier;
 195  
         do {
 196  70
             modifier = modifierIterator.next();
 197  70
         } while (modifierIterator.hasNext() && modifier.getType() == TokenTypes.ANNOTATION);
 198  57
         return modifier;
 199  
     }
 200  
 
 201  
     /**
 202  
      * Checks whether annotation on type takes place.
 203  
      * @param modifier modifier token.
 204  
      * @return true if annotation on type takes place.
 205  
      */
 206  
     private static boolean isAnnotationOnType(DetailAST modifier) {
 207  13
         boolean annotationOnType = false;
 208  13
         final DetailAST modifiers = modifier.getParent();
 209  13
         final DetailAST definition = modifiers.getParent();
 210  13
         final int definitionType = definition.getType();
 211  13
         if (definitionType == TokenTypes.VARIABLE_DEF
 212  
                 || definitionType == TokenTypes.PARAMETER_DEF
 213  
                 || definitionType == TokenTypes.CTOR_DEF) {
 214  4
             annotationOnType = true;
 215  
         }
 216  9
         else if (definitionType == TokenTypes.METHOD_DEF) {
 217  8
             final DetailAST typeToken = definition.findFirstToken(TokenTypes.TYPE);
 218  8
             final int methodReturnType = typeToken.getLastChild().getType();
 219  8
             if (methodReturnType != TokenTypes.LITERAL_VOID) {
 220  4
                 annotationOnType = true;
 221  
             }
 222  
         }
 223  13
         return annotationOnType;
 224  
     }
 225  
 }