Coverage Report - com.puppycrawl.tools.checkstyle.checks.UncommentedMainCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
UncommentedMainCheck
90%
54/60
83%
35/42
2.786
 
 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;
 20  
 
 21  
 import com.puppycrawl.tools.checkstyle.api.Check;
 22  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 23  
 import com.puppycrawl.tools.checkstyle.api.FullIdent;
 24  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 25  
 import com.puppycrawl.tools.checkstyle.api.Utils;
 26  
 
 27  
 import java.util.regex.Pattern;
 28  
 import java.util.regex.PatternSyntaxException;
 29  
 
 30  
 import org.apache.commons.beanutils.ConversionException;
 31  
 
 32  
 /**
 33  
  * Detects uncommented main methods. Basically detects
 34  
  * any main method, since if it is detectable
 35  
  * that means it is uncommented.
 36  
  *
 37  
  * <pre class="body">
 38  
  * &lt;module name=&quot;UncommentedMain&quot;/&gt;
 39  
  * </pre>
 40  
  *
 41  
  * @author Michael Yui
 42  
  * @author o_sukhodolsky
 43  
  */
 44  2
 public class UncommentedMainCheck
 45  
     extends Check
 46  
 {
 47  
     /** the pattern to exclude classes from the check */
 48  2
     private String mExcludedClasses = "^$";
 49  
     /** compiled regexp to exclude classes from check */
 50  2
     private Pattern mExcludedClassesPattern =
 51  
         Utils.createPattern(mExcludedClasses);
 52  
     /** current class name */
 53  
     private String mCurrentClass;
 54  
     /** current package */
 55  
     private FullIdent mPackage;
 56  
     /** class definition depth */
 57  
     private int mClassDepth;
 58  
 
 59  
     /**
 60  
      * Set the excluded classes pattern.
 61  
      * @param aExcludedClasses a <code>String</code> value
 62  
      * @throws ConversionException unable to parse aExcludedClasses
 63  
      */
 64  
     public void setExcludedClasses(String aExcludedClasses)
 65  
         throws ConversionException
 66  
     {
 67  
         try {
 68  1
             mExcludedClasses = aExcludedClasses;
 69  1
             mExcludedClassesPattern = Utils.getPattern(mExcludedClasses);
 70  
         }
 71  0
         catch (final PatternSyntaxException e) {
 72  0
             throw new ConversionException("unable to parse "
 73  
                                           + mExcludedClasses,
 74  
                                           e);
 75  1
         }
 76  1
     }
 77  
 
 78  
     @Override
 79  
     public int[] getDefaultTokens()
 80  
     {
 81  2
         return new int[] {
 82  
             TokenTypes.METHOD_DEF,
 83  
             TokenTypes.CLASS_DEF,
 84  
             TokenTypes.PACKAGE_DEF,
 85  
         };
 86  
     }
 87  
 
 88  
     @Override
 89  
     public int[] getRequiredTokens()
 90  
     {
 91  0
         return getDefaultTokens();
 92  
     }
 93  
 
 94  
     @Override
 95  
     public void beginTree(DetailAST aRootAST)
 96  
     {
 97  2
         mPackage = FullIdent.createFullIdent(null);
 98  2
         mCurrentClass = null;
 99  2
         mClassDepth = 0;
 100  2
     }
 101  
 
 102  
     @Override
 103  
     public void leaveToken(DetailAST aAst)
 104  
     {
 105  38
         if (aAst.getType() == TokenTypes.CLASS_DEF) {
 106  18
             if (mClassDepth == 1) {
 107  18
                 mCurrentClass = null;
 108  
             }
 109  18
             mClassDepth--;
 110  
         }
 111  38
     }
 112  
 
 113  
     @Override
 114  
     public void visitToken(DetailAST aAst)
 115  
     {
 116  38
         switch (aAst.getType()) {
 117  
         case TokenTypes.PACKAGE_DEF:
 118  2
             visitPackageDef(aAst);
 119  2
             break;
 120  
         case TokenTypes.CLASS_DEF:
 121  18
             visitClassDef(aAst);
 122  18
             break;
 123  
         case TokenTypes.METHOD_DEF:
 124  18
             visitMethodDef(aAst);
 125  18
             break;
 126  
         default:
 127  0
             throw new IllegalStateException(aAst.toString());
 128  
         }
 129  38
     }
 130  
 
 131  
     /**
 132  
      * Sets current package.
 133  
      * @param aPackage node for package definition
 134  
      */
 135  
     private void visitPackageDef(DetailAST aPackage)
 136  
     {
 137  2
         mPackage = FullIdent.createFullIdent(aPackage.getLastChild()
 138  
                 .getPreviousSibling());
 139  2
     }
 140  
 
 141  
     /**
 142  
      * If not inner class then change current class name.
 143  
      * @param aClass node for class definition
 144  
      */
 145  
     private void visitClassDef(DetailAST aClass)
 146  
     {
 147  
         // we are not use inner classes because they can not
 148  
         // have static methods
 149  18
         if (mClassDepth == 0) {
 150  18
             final DetailAST ident = aClass.findFirstToken(TokenTypes.IDENT);
 151  18
             mCurrentClass = mPackage.getText() + "." + ident.getText();
 152  18
             mClassDepth++;
 153  
         }
 154  18
     }
 155  
 
 156  
     /**
 157  
      * Checks method definition if this is
 158  
      * <code>public static void main(String[])</code>.
 159  
      * @param aMethod method definition node
 160  
      */
 161  
     private void visitMethodDef(DetailAST aMethod)
 162  
     {
 163  18
         if (mClassDepth != 1) {
 164  
             // method in inner class or in interface definition
 165  0
             return;
 166  
         }
 167  
 
 168  18
         if (checkClassName()
 169  
             && checkName(aMethod)
 170  
             && checkModifiers(aMethod)
 171  
             && checkType(aMethod)
 172  
             && checkParams(aMethod))
 173  
         {
 174  5
             log(aMethod.getLineNo(), "uncommented.main");
 175  
         }
 176  18
     }
 177  
 
 178  
     /**
 179  
      * Checks that current class is not excluded
 180  
      * @return true if check passed, false otherwise
 181  
      */
 182  
     private boolean checkClassName()
 183  
     {
 184  18
         return !mExcludedClassesPattern.matcher(mCurrentClass).find();
 185  
     }
 186  
 
 187  
     /**
 188  
      * Checks that method name is @quot;main@quot;.
 189  
      * @param aMethod the METHOD_DEF node
 190  
      * @return true if check passed, false otherwise
 191  
      */
 192  
     private boolean checkName(DetailAST aMethod)
 193  
     {
 194  17
         final DetailAST ident = aMethod.findFirstToken(TokenTypes.IDENT);
 195  17
         return "main".equals(ident.getText());
 196  
     }
 197  
 
 198  
     /**
 199  
      * Checks that method has final and static modifiers.
 200  
      * @param aMethod the METHOD_DEF node
 201  
      * @return true if check passed, false otherwise
 202  
      */
 203  
     private boolean checkModifiers(DetailAST aMethod)
 204  
     {
 205  17
         final DetailAST modifiers =
 206  
             aMethod.findFirstToken(TokenTypes.MODIFIERS);
 207  
 
 208  17
         return modifiers.branchContains(TokenTypes.LITERAL_PUBLIC)
 209  
             && modifiers.branchContains(TokenTypes.LITERAL_STATIC);
 210  
     }
 211  
 
 212  
     /**
 213  
      * Checks that return type is <code>void</code>.
 214  
      * @param aMethod the METHOD_DEF node
 215  
      * @return true if check passed, false otherwise
 216  
      */
 217  
     private boolean checkType(DetailAST aMethod)
 218  
     {
 219  13
         final DetailAST type =
 220  
             aMethod.findFirstToken(TokenTypes.TYPE).getFirstChild();
 221  13
         return type.getType() == TokenTypes.LITERAL_VOID;
 222  
     }
 223  
 
 224  
     /**
 225  
      * Checks that method has only <code>String[]</code> param
 226  
      * @param aMethod the METHOD_DEF node
 227  
      * @return true if check passed, false otherwise
 228  
      */
 229  
     private boolean checkParams(DetailAST aMethod)
 230  
     {
 231  11
         final DetailAST params = aMethod.findFirstToken(TokenTypes.PARAMETERS);
 232  11
         if (params.getChildCount() != 1) {
 233  4
             return false;
 234  
         }
 235  7
         final DetailAST paramType = (params.getFirstChild())
 236  
             .findFirstToken(TokenTypes.TYPE);
 237  7
         final DetailAST arrayDecl =
 238  
             paramType.findFirstToken(TokenTypes.ARRAY_DECLARATOR);
 239  7
         if (arrayDecl == null) {
 240  2
             return false;
 241  
         }
 242  
 
 243  5
         final DetailAST arrayType = arrayDecl.getFirstChild();
 244  
 
 245  5
         if ((arrayType.getType() == TokenTypes.IDENT)
 246  
             || (arrayType.getType() == TokenTypes.DOT))
 247  
         {
 248  5
             final FullIdent type = FullIdent.createFullIdent(arrayType);
 249  5
             return ("String".equals(type.getText())
 250  
                     || "java.lang.String".equals(type.getText()));
 251  
         }
 252  
 
 253  0
         return false;
 254  
     }
 255  
 }