Coverage Report - com.puppycrawl.tools.checkstyle.checks.design.OneTopLevelClassCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
OneTopLevelClassCheck
100%
29/29
100%
18/18
2.333
 
 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.design;
 21  
 
 22  
 import java.util.Map;
 23  
 import java.util.SortedMap;
 24  
 import java.util.TreeMap;
 25  
 
 26  
 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
 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  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 31  
 
 32  
 /**
 33  
  * Checks that each top-level class, interface
 34  
  * or enum resides in a source file of its own.
 35  
  * <p>
 36  
  * Official description of a 'top-level' term:<a
 37  
  * href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-7.html#jls-7.6">
 38  
  * 7.6. Top Level Type Declarations</a>. If file doesn't contains
 39  
  * public class, enum or interface, top-level type is the first type in file.
 40  
  * </p>
 41  
  * <p>
 42  
  * An example of code with violations:
 43  
  * </p>
 44  
  * <pre>{@code
 45  
  * public class Foo{
 46  
  *     //methods
 47  
  * }
 48  
  *
 49  
  * class Foo2{
 50  
  *     //methods
 51  
  * }
 52  
  * }</pre>
 53  
  * <p>
 54  
  * An example of code without top-level public type:
 55  
  * </p>
 56  
  * <pre>{@code
 57  
  * class Foo{ //top-level class
 58  
  *     //methods
 59  
  * }
 60  
  *
 61  
  * class Foo2{
 62  
  *     //methods
 63  
  * }
 64  
  * }</pre>
 65  
  * <p>
 66  
  * An example of check's configuration:
 67  
  * </p>
 68  
  * <pre>
 69  
  * &lt;module name="OneTopLevelClass"/&gt;
 70  
  * </pre>
 71  
  *
 72  
  * <p>
 73  
  * An example of code without violations:
 74  
  * </p>
 75  
  * <pre>{@code
 76  
  * public class Foo{
 77  
  *     //methods
 78  
  * }
 79  
  * }</pre>
 80  
  *
 81  
  * <p> ATTENTION: This Check does not support customization of validated tokens,
 82  
  *  so do not use the "tokens" property.
 83  
  * </p>
 84  
  *
 85  
  * @author maxvetrenko
 86  
  */
 87  
 @FileStatefulCheck
 88  21
 public class OneTopLevelClassCheck extends AbstractCheck {
 89  
 
 90  
     /**
 91  
      * A key is pointing to the warning message text in "messages.properties"
 92  
      * file.
 93  
      */
 94  
     public static final String MSG_KEY = "one.top.level.class";
 95  
 
 96  
     /**
 97  
      * True if a java source file contains a type
 98  
      * with a public access level modifier.
 99  
      */
 100  
     private boolean publicTypeFound;
 101  
 
 102  
     /** Mapping between type names and line numbers of the type declarations.*/
 103  21
     private final SortedMap<Integer, String> lineNumberTypeMap = new TreeMap<>();
 104  
 
 105  
     @Override
 106  
     public int[] getDefaultTokens() {
 107  18
         return getRequiredTokens();
 108  
     }
 109  
 
 110  
     @Override
 111  
     public int[] getAcceptableTokens() {
 112  7
         return getRequiredTokens();
 113  
     }
 114  
 
 115  
     // ZERO tokens as Check do Traverse of Tree himself, he does not need to subscribed to Tokens
 116  
     @Override
 117  
     public int[] getRequiredTokens() {
 118  44
         return CommonUtils.EMPTY_INT_ARRAY;
 119  
     }
 120  
 
 121  
     @Override
 122  
     public void beginTree(DetailAST rootAST) {
 123  13
         publicTypeFound = false;
 124  13
         lineNumberTypeMap.clear();
 125  
 
 126  13
         DetailAST currentNode = rootAST;
 127  54
         while (currentNode != null) {
 128  41
             if (currentNode.getType() == TokenTypes.CLASS_DEF
 129  26
                     || currentNode.getType() == TokenTypes.ENUM_DEF
 130  20
                     || currentNode.getType() == TokenTypes.INTERFACE_DEF) {
 131  28
                 if (isPublic(currentNode)) {
 132  10
                     publicTypeFound = true;
 133  
                 }
 134  
                 else {
 135  18
                     final String typeName = currentNode
 136  18
                             .findFirstToken(TokenTypes.IDENT).getText();
 137  18
                     lineNumberTypeMap.put(currentNode.getLineNo(), typeName);
 138  
                 }
 139  
             }
 140  41
             currentNode = currentNode.getNextSibling();
 141  
         }
 142  13
     }
 143  
 
 144  
     @Override
 145  
     public void finishTree(DetailAST rootAST) {
 146  13
         if (!lineNumberTypeMap.isEmpty()) {
 147  8
             if (!publicTypeFound) {
 148  
                 // skip first top-level type.
 149  1
                 lineNumberTypeMap.remove(lineNumberTypeMap.firstKey());
 150  
             }
 151  
 
 152  
             for (Map.Entry<Integer, String> entry
 153  8
                     : lineNumberTypeMap.entrySet()) {
 154  17
                 log(entry.getKey(), MSG_KEY, entry.getValue());
 155  17
             }
 156  
         }
 157  13
     }
 158  
 
 159  
     /**
 160  
      * Checks if a type is public.
 161  
      * @param typeDef type definition node.
 162  
      * @return true if a type has a public access level modifier.
 163  
      */
 164  
     private static boolean isPublic(DetailAST typeDef) {
 165  28
         final DetailAST modifiers =
 166  28
                 typeDef.findFirstToken(TokenTypes.MODIFIERS);
 167  28
         return modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) != null;
 168  
     }
 169  
 }