Coverage Report - com.puppycrawl.tools.checkstyle.checks.design.HideUtilityClassConstructorCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
HideUtilityClassConstructorCheck
100%
38/38
95%
40/42
5.75
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2014  Oliver Burn
 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  
 package com.puppycrawl.tools.checkstyle.checks.design;
 20  
 
 21  
 import com.puppycrawl.tools.checkstyle.api.Check;
 22  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 23  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 24  
 
 25  
 /**
 26  
  * Make sure that utility classes (classes that contain only static methods)
 27  
  * do not have a public constructor.
 28  
  * <p>
 29  
  * Rationale: Instantiating utility classes does not make sense.
 30  
  * A common mistake is forgetting to hide the default constructor.
 31  
  * </p>
 32  
  *
 33  
  * @author lkuehne
 34  
  */
 35  7
 public class HideUtilityClassConstructorCheck extends Check
 36  
 {
 37  
     @Override
 38  
     public int[] getDefaultTokens()
 39  
     {
 40  7
         return new int[] {TokenTypes.CLASS_DEF};
 41  
     }
 42  
 
 43  
     @Override
 44  
     public void visitToken(DetailAST aAST)
 45  
     {
 46  12
         if (isAbstract(aAST)) {
 47  
             // abstract class could not have private constructor
 48  2
             return;
 49  
         }
 50  
 
 51  10
         final DetailAST objBlock = aAST.findFirstToken(TokenTypes.OBJBLOCK);
 52  10
         DetailAST child = objBlock.getFirstChild();
 53  10
         final boolean hasStaticModifier = isStatic(aAST);
 54  10
         boolean hasMethodOrField = false;
 55  10
         boolean hasNonStaticMethodOrField = false;
 56  10
         boolean hasNonPrivateStaticMethodOrField = false;
 57  10
         boolean hasDefaultCtor = true;
 58  10
         boolean hasPublicCtor = false;
 59  
 
 60  58
         while (child != null) {
 61  48
             final int type = child.getType();
 62  48
             if (type == TokenTypes.METHOD_DEF
 63  
                     || type == TokenTypes.VARIABLE_DEF)
 64  
             {
 65  21
                 hasMethodOrField = true;
 66  21
                 final DetailAST modifiers =
 67  
                     child.findFirstToken(TokenTypes.MODIFIERS);
 68  21
                 final boolean isStatic =
 69  
                     modifiers.branchContains(TokenTypes.LITERAL_STATIC);
 70  21
                 final boolean isPrivate =
 71  
                     modifiers.branchContains(TokenTypes.LITERAL_PRIVATE);
 72  
 
 73  21
                 if (!isStatic && !isPrivate) {
 74  7
                     hasNonStaticMethodOrField = true;
 75  
                 }
 76  21
                 if (isStatic && !isPrivate) {
 77  6
                     hasNonPrivateStaticMethodOrField = true;
 78  
                 }
 79  
             }
 80  48
             if (type == TokenTypes.CTOR_DEF) {
 81  4
                 hasDefaultCtor = false;
 82  4
                 final DetailAST modifiers =
 83  
                     child.findFirstToken(TokenTypes.MODIFIERS);
 84  4
                 if (!modifiers.branchContains(TokenTypes.LITERAL_PRIVATE)
 85  
                     && !modifiers.branchContains(TokenTypes.LITERAL_PROTECTED))
 86  
                 {
 87  
                     // treat package visible as public
 88  
                     // for the purpose of this Check
 89  3
                     hasPublicCtor = true;
 90  
                 }
 91  
 
 92  
             }
 93  48
             child = child.getNextSibling();
 94  48
         }
 95  
 
 96  10
         final boolean hasAccessibleCtor = (hasDefaultCtor || hasPublicCtor);
 97  
 
 98  
         // figure out if class extends java.lang.object directly
 99  
         // keep it simple for now and get a 99% solution
 100  
         // TODO: check for "extends java.lang.Object" and "extends Object"
 101  
         // consider "import org.omg.CORBA.*"
 102  10
         final boolean extendsJLO = // J.Lo even made it into in our sources :-)
 103  
             aAST.findFirstToken(TokenTypes.EXTENDS_CLAUSE) == null;
 104  
 
 105  10
         final boolean isUtilClass = extendsJLO && hasMethodOrField
 106  
             && !hasNonStaticMethodOrField && hasNonPrivateStaticMethodOrField;
 107  
 
 108  10
         if (isUtilClass && (hasAccessibleCtor && !hasStaticModifier)) {
 109  1
             log(aAST.getLineNo(), aAST.getColumnNo(), "hide.utility.class");
 110  
         }
 111  10
     }
 112  
 
 113  
     /**
 114  
      * @param aAST class definition for check.
 115  
      * @return true if a given class declared as abstract.
 116  
      */
 117  
     private boolean isAbstract(DetailAST aAST)
 118  
     {
 119  12
         return aAST.findFirstToken(TokenTypes.MODIFIERS)
 120  
             .branchContains(TokenTypes.ABSTRACT);
 121  
     }
 122  
 
 123  
     /**
 124  
      * @param aAST class definition for check.
 125  
      * @return true if a given class declared as static.
 126  
      */
 127  
     private boolean isStatic(DetailAST aAST)
 128  
     {
 129  10
         return aAST.findFirstToken(TokenTypes.MODIFIERS)
 130  
             .branchContains(TokenTypes.LITERAL_STATIC);
 131  
     }
 132  
 }