Coverage Report - com.puppycrawl.tools.checkstyle.checks.design.VisibilityModifierCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
VisibilityModifierCheck
92%
47/51
92%
35/38
3.083
 
 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 antlr.collections.AST;
 22  
 import com.google.common.collect.Sets;
 23  
 import com.puppycrawl.tools.checkstyle.api.Check;
 24  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 25  
 import com.puppycrawl.tools.checkstyle.api.ScopeUtils;
 26  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 27  
 import com.puppycrawl.tools.checkstyle.api.Utils;
 28  
 import java.util.Set;
 29  
 import java.util.regex.Pattern;
 30  
 import java.util.regex.PatternSyntaxException;
 31  
 import org.apache.commons.beanutils.ConversionException;
 32  
 
 33  
 /**
 34  
  * Checks visibility of class members. Only static final members may be public,
 35  
  * other class members must be private unless allowProtected/Package is set.
 36  
  * <p>
 37  
  * Public members are not flagged if the name matches the public
 38  
  * member regular expression (contains "^serialVersionUID$" by
 39  
  * default).
 40  
  * </p>
 41  
  * Rationale: Enforce encapsulation.
 42  
  *
 43  
  * @author lkuehne
 44  
  */
 45  
 public class VisibilityModifierCheck
 46  
     extends Check
 47  
 {
 48  
     /** whether protected members are allowed */
 49  
     private boolean mProtectedAllowed;
 50  
 
 51  
     /** whether package visible members are allowed */
 52  
     private boolean mPackageAllowed;
 53  
 
 54  
     /**
 55  
      * pattern for public members that should be ignored.  Note:
 56  
      * Earlier versions of checkstyle used ^f[A-Z][a-zA-Z0-9]*$ as the
 57  
      * default to allow CMP for EJB 1.1 with the default settings.
 58  
      * With EJB 2.0 it is not longer necessary to have public access
 59  
      * for persistent fields.
 60  
      */
 61  4
     private String mPublicMemberFormat = "^serialVersionUID$";
 62  
 
 63  
     /** regexp for public members that should be ignored */
 64  
     private Pattern mPublicMemberPattern;
 65  
 
 66  
     /** Create an instance. */
 67  
     public VisibilityModifierCheck()
 68  4
     {
 69  4
         setPublicMemberPattern(mPublicMemberFormat);
 70  4
     }
 71  
 
 72  
     /** @return whether protected members are allowed */
 73  
     public boolean isProtectedAllowed()
 74  
     {
 75  7
         return mProtectedAllowed;
 76  
     }
 77  
 
 78  
     /**
 79  
      * Set whether protected members are allowed.
 80  
      * @param aProtectedAllowed whether protected members are allowed
 81  
      */
 82  
     public void setProtectedAllowed(boolean aProtectedAllowed)
 83  
     {
 84  1
         mProtectedAllowed = aProtectedAllowed;
 85  1
     }
 86  
 
 87  
     /** @return whether package visible members are allowed */
 88  
     public boolean isPackageAllowed()
 89  
     {
 90  7
         return mPackageAllowed;
 91  
     }
 92  
 
 93  
     /**
 94  
      * Set whether package visible members are allowed.
 95  
      * @param aPackageAllowed whether package visible members are allowed
 96  
      */
 97  
     public void setPackageAllowed(boolean aPackageAllowed)
 98  
     {
 99  1
         mPackageAllowed = aPackageAllowed;
 100  1
     }
 101  
 
 102  
     /**
 103  
      * Set the pattern for public members to ignore.
 104  
      * @param aPattern pattern for public members to ignore.
 105  
      */
 106  
     public void setPublicMemberPattern(String aPattern)
 107  
     {
 108  
         try {
 109  8
             mPublicMemberPattern = Utils.getPattern(aPattern);
 110  8
             mPublicMemberFormat = aPattern;
 111  
         }
 112  0
         catch (final PatternSyntaxException e) {
 113  0
             throw new ConversionException("unable to parse " + aPattern, e);
 114  8
         }
 115  8
     }
 116  
 
 117  
     /**
 118  
      * @return the regexp for public members to ignore.
 119  
      */
 120  
     private Pattern getPublicMemberRegexp()
 121  
     {
 122  9
         return mPublicMemberPattern;
 123  
     }
 124  
 
 125  
     @Override
 126  
     public int[] getDefaultTokens()
 127  
     {
 128  4
         return new int[] {TokenTypes.VARIABLE_DEF};
 129  
     }
 130  
 
 131  
     @Override
 132  
     public void visitToken(DetailAST aAST)
 133  
     {
 134  63
         if ((aAST.getType() != TokenTypes.VARIABLE_DEF)
 135  
             || (aAST.getParent().getType() != TokenTypes.OBJBLOCK))
 136  
         {
 137  19
             return;
 138  
         }
 139  
 
 140  44
         final DetailAST varNameAST = getVarNameAST(aAST);
 141  44
         final String varName = varNameAST.getText();
 142  44
         final boolean inInterfaceOrAnnotationBlock =
 143  
             ScopeUtils.inInterfaceOrAnnotationBlock(aAST);
 144  44
         final Set<String> mods = getModifiers(aAST);
 145  44
         final String declaredScope = getVisibilityScope(mods);
 146  44
         final String variableScope =
 147  
              inInterfaceOrAnnotationBlock ? "public" : declaredScope;
 148  
 
 149  44
         if (!("private".equals(variableScope)
 150  
                 || inInterfaceOrAnnotationBlock // implicitly static and final
 151  
                 || (mods.contains("static") && mods.contains("final"))
 152  
                 || ("package".equals(variableScope) && isPackageAllowed())
 153  
                 || ("protected".equals(variableScope) && isProtectedAllowed())
 154  
                 || ("public".equals(variableScope)
 155  
                    && getPublicMemberRegexp().matcher(varName).find())))
 156  
         {
 157  17
             log(varNameAST.getLineNo(), varNameAST.getColumnNo(),
 158  
                     "variable.notPrivate", varName);
 159  
         }
 160  44
     }
 161  
 
 162  
     /**
 163  
      * Returns the variable name in a VARIABLE_DEF AST.
 164  
      * @param aVariableDefAST an AST where type == VARIABLE_DEF AST.
 165  
      * @return the variable name in aVariableDefAST
 166  
      */
 167  
     private DetailAST getVarNameAST(DetailAST aVariableDefAST)
 168  
     {
 169  44
         DetailAST ast = aVariableDefAST.getFirstChild();
 170  88
         while (ast != null) {
 171  88
             final DetailAST nextSibling = ast.getNextSibling();
 172  88
             if (ast.getType() == TokenTypes.TYPE) {
 173  44
                 return nextSibling;
 174  
             }
 175  44
             ast = nextSibling;
 176  44
         }
 177  0
         return null;
 178  
     }
 179  
 
 180  
     /**
 181  
      * Returns the set of modifier Strings for a VARIABLE_DEF AST.
 182  
      * @param aVariableDefAST AST for a vraiable definition
 183  
      * @return the set of modifier Strings for variableDefAST
 184  
      */
 185  
     private Set<String> getModifiers(DetailAST aVariableDefAST)
 186  
     {
 187  44
         final AST modifiersAST = aVariableDefAST.getFirstChild();
 188  44
         if (modifiersAST.getType() != TokenTypes.MODIFIERS) {
 189  0
             throw new IllegalStateException("Strange parse tree");
 190  
         }
 191  44
         final Set<String> retVal = Sets.newHashSet();
 192  44
         AST modifier = modifiersAST.getFirstChild();
 193  95
         while (modifier != null) {
 194  51
             retVal.add(modifier.getText());
 195  51
             modifier = modifier.getNextSibling();
 196  
         }
 197  44
         return retVal;
 198  
 
 199  
     }
 200  
 
 201  
     /**
 202  
      * Returns the visibility scope specified with a set of modifiers.
 203  
      * @param aModifiers the set of modifier Strings
 204  
      * @return one of "public", "private", "protected", "package"
 205  
      */
 206  
     private String getVisibilityScope(Set<String> aModifiers)
 207  
     {
 208  44
         final String[] explicitModifiers = {"public", "private", "protected"};
 209  108
         for (final String candidate : explicitModifiers) {
 210  96
             if (aModifiers.contains(candidate)) {
 211  32
                 return candidate;
 212  
             }
 213  
         }
 214  12
         return "package";
 215  
     }
 216  
 }