Coverage Report - com.puppycrawl.tools.checkstyle.checks.design.HideUtilityClassConstructorCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
HideUtilityClassConstructorCheck
100%
25/25
100%
26/26
2.308
HideUtilityClassConstructorCheck$Details
100%
39/39
100%
22/22
2.308
 
 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 com.puppycrawl.tools.checkstyle.StatelessCheck;
 23  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 24  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 25  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 26  
 
 27  
 /**
 28  
  * Make sure that utility classes (classes that contain only static methods)
 29  
  * do not have a public constructor.
 30  
  * <p>
 31  
  * Rationale: Instantiating utility classes does not make sense.
 32  
  * A common mistake is forgetting to hide the default constructor.
 33  
  * </p>
 34  
  *
 35  
  * @author lkuehne
 36  
  */
 37  
 @StatelessCheck
 38  19
 public class HideUtilityClassConstructorCheck extends AbstractCheck {
 39  
 
 40  
     /**
 41  
      * A key is pointing to the warning message text in "messages.properties"
 42  
      * file.
 43  
      */
 44  
     public static final String MSG_KEY = "hide.utility.class";
 45  
 
 46  
     @Override
 47  
     public int[] getDefaultTokens() {
 48  28
         return getRequiredTokens();
 49  
     }
 50  
 
 51  
     @Override
 52  
     public int[] getAcceptableTokens() {
 53  5
         return getRequiredTokens();
 54  
     }
 55  
 
 56  
     @Override
 57  
     public int[] getRequiredTokens() {
 58  62
         return new int[] {TokenTypes.CLASS_DEF};
 59  
     }
 60  
 
 61  
     @Override
 62  
     public void visitToken(DetailAST ast) {
 63  
         // abstract class could not have private constructor
 64  19
         if (!isAbstract(ast)) {
 65  17
             final boolean hasStaticModifier = isStatic(ast);
 66  
 
 67  17
             final Details details = new Details(ast);
 68  17
             details.invoke();
 69  
 
 70  17
             final boolean hasDefaultCtor = details.isHasDefaultCtor();
 71  17
             final boolean hasPublicCtor = details.isHasPublicCtor();
 72  17
             final boolean hasMethodOrField = details.isHasMethodOrField();
 73  17
             final boolean hasNonStaticMethodOrField = details.isHasNonStaticMethodOrField();
 74  17
             final boolean hasNonPrivateStaticMethodOrField =
 75  17
                     details.isHasNonPrivateStaticMethodOrField();
 76  
 
 77  17
             final boolean hasAccessibleCtor = hasDefaultCtor || hasPublicCtor;
 78  
 
 79  
             // figure out if class extends java.lang.object directly
 80  
             // keep it simple for now and get a 99% solution
 81  17
             final boolean extendsJlo =
 82  17
                 ast.findFirstToken(TokenTypes.EXTENDS_CLAUSE) == null;
 83  
 
 84  17
             final boolean isUtilClass = extendsJlo && hasMethodOrField
 85  
                 && !hasNonStaticMethodOrField && hasNonPrivateStaticMethodOrField;
 86  
 
 87  17
             if (isUtilClass && hasAccessibleCtor && !hasStaticModifier) {
 88  2
                 log(ast.getLineNo(), ast.getColumnNo(), MSG_KEY);
 89  
             }
 90  
         }
 91  19
     }
 92  
 
 93  
     /**
 94  
      * Returns true if given class is abstract or false.
 95  
      * @param ast class definition for check.
 96  
      * @return true if a given class declared as abstract.
 97  
      */
 98  
     private static boolean isAbstract(DetailAST ast) {
 99  38
         return ast.findFirstToken(TokenTypes.MODIFIERS)
 100  19
             .findFirstToken(TokenTypes.ABSTRACT) != null;
 101  
     }
 102  
 
 103  
     /**
 104  
      * Returns true if given class is static or false.
 105  
      * @param ast class definition for check.
 106  
      * @return true if a given class declared as static.
 107  
      */
 108  
     private static boolean isStatic(DetailAST ast) {
 109  34
         return ast.findFirstToken(TokenTypes.MODIFIERS)
 110  17
             .findFirstToken(TokenTypes.LITERAL_STATIC) != null;
 111  
     }
 112  
 
 113  
     /**
 114  
      * Details of class that are required for validation.
 115  
      */
 116  
     private static class Details {
 117  
         /** Class ast. */
 118  
         private final DetailAST ast;
 119  
         /** Result of details gathering. */
 120  
         private boolean hasMethodOrField;
 121  
         /** Result of details gathering. */
 122  
         private boolean hasNonStaticMethodOrField;
 123  
         /** Result of details gathering. */
 124  
         private boolean hasNonPrivateStaticMethodOrField;
 125  
         /** Result of details gathering. */
 126  
         private boolean hasDefaultCtor;
 127  
         /** Result of details gathering. */
 128  
         private boolean hasPublicCtor;
 129  
 
 130  
         /**
 131  
          * C-tor.
 132  
          * @param ast class ast
 133  
          * */
 134  17
         Details(DetailAST ast) {
 135  17
             this.ast = ast;
 136  17
         }
 137  
 
 138  
         /**
 139  
          * Getter.
 140  
          * @return boolean
 141  
          */
 142  
         public boolean isHasMethodOrField() {
 143  17
             return hasMethodOrField;
 144  
         }
 145  
 
 146  
         /**
 147  
          * Getter.
 148  
          * @return boolean
 149  
          */
 150  
         public boolean isHasNonStaticMethodOrField() {
 151  17
             return hasNonStaticMethodOrField;
 152  
         }
 153  
 
 154  
         /**
 155  
          * Getter.
 156  
          * @return boolean
 157  
          */
 158  
         public boolean isHasNonPrivateStaticMethodOrField() {
 159  17
             return hasNonPrivateStaticMethodOrField;
 160  
         }
 161  
 
 162  
         /**
 163  
          * Getter.
 164  
          * @return boolean
 165  
          */
 166  
         public boolean isHasDefaultCtor() {
 167  17
             return hasDefaultCtor;
 168  
         }
 169  
 
 170  
         /**
 171  
          * Getter.
 172  
          * @return boolean
 173  
          */
 174  
         public boolean isHasPublicCtor() {
 175  17
             return hasPublicCtor;
 176  
         }
 177  
 
 178  
         /**
 179  
          * Main method to gather statistics.
 180  
          */
 181  
         public void invoke() {
 182  17
             final DetailAST objBlock = ast.findFirstToken(TokenTypes.OBJBLOCK);
 183  17
             hasMethodOrField = false;
 184  17
             hasNonStaticMethodOrField = false;
 185  17
             hasNonPrivateStaticMethodOrField = false;
 186  17
             hasDefaultCtor = true;
 187  17
             hasPublicCtor = false;
 188  17
             DetailAST child = objBlock.getFirstChild();
 189  
 
 190  89
             while (child != null) {
 191  72
                 final int type = child.getType();
 192  72
                 if (type == TokenTypes.METHOD_DEF
 193  
                         || type == TokenTypes.VARIABLE_DEF) {
 194  25
                     hasMethodOrField = true;
 195  25
                     final DetailAST modifiers =
 196  25
                         child.findFirstToken(TokenTypes.MODIFIERS);
 197  25
                     final boolean isStatic =
 198  25
                         modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
 199  25
                     final boolean isPrivate =
 200  25
                         modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) != null;
 201  
 
 202  25
                     if (!isStatic) {
 203  12
                         hasNonStaticMethodOrField = true;
 204  
                     }
 205  25
                     if (isStatic && !isPrivate) {
 206  8
                         hasNonPrivateStaticMethodOrField = true;
 207  
                     }
 208  
                 }
 209  72
                 if (type == TokenTypes.CTOR_DEF) {
 210  9
                     hasDefaultCtor = false;
 211  9
                     final DetailAST modifiers =
 212  9
                         child.findFirstToken(TokenTypes.MODIFIERS);
 213  9
                     if (modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null
 214  6
                         && modifiers.findFirstToken(TokenTypes.LITERAL_PROTECTED) == null) {
 215  
                         // treat package visible as public
 216  
                         // for the purpose of this Check
 217  5
                         hasPublicCtor = true;
 218  
                     }
 219  
 
 220  
                 }
 221  72
                 child = child.getNextSibling();
 222  72
             }
 223  17
         }
 224  
     }
 225  
 }