Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/puppycrawl/tools/checkstyle/checks/design/VisibilityModifierCheck.java


1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2003  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 java.util.HashSet;
22  import java.util.Set;
23  
24  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
25  import com.puppycrawl.tools.checkstyle.api.Check;
26  import com.puppycrawl.tools.checkstyle.api.DetailAST;
27  import com.puppycrawl.tools.checkstyle.api.Utils;
28  import org.apache.commons.beanutils.ConversionException;
29  import org.apache.regexp.RE;
30  import org.apache.regexp.RESyntaxException;
31  import antlr.collections.AST;
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      private String mPublicMemberPattern = "^serialVersionUID$";
62  
63      /** regexp for public members that should be ignored */
64      private RE mPublicMemberRE;
65  
66      /** Create an instance. */
67      public VisibilityModifierCheck()
68      {
69          setPublicMemberPattern(mPublicMemberPattern);
70      }
71  
72      /** @return whether protected members are allowed */
73      public boolean isProtectedAllowed()
74      {
75          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          mProtectedAllowed = aProtectedAllowed;
85      }
86  
87      /** @return whether package visible members are allowed */
88      public boolean isPackageAllowed()
89      {
90          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          mPackageAllowed = aPackageAllowed;
100     }
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             mPublicMemberRE = Utils.getRE(aPattern);
110             mPublicMemberPattern = aPattern;
111         }
112         catch (RESyntaxException e) {
113             throw new ConversionException("unable to parse " + aPattern, e);
114         }
115     }
116 
117     /**
118      * @return the regexp for public members to ignore.
119      */
120     private RE getPublicMemberRegexp()
121     {
122         return mPublicMemberRE;
123     }
124 
125     /** @see Check */
126     public int[] getDefaultTokens()
127     {
128         return new int[] {TokenTypes.VARIABLE_DEF};
129     }
130 
131     /** @see Check */
132     public void visitToken(DetailAST aAST)
133     {
134         if ((aAST.getType() != TokenTypes.VARIABLE_DEF)
135             || (aAST.getParent().getType() != TokenTypes.OBJBLOCK))
136         {
137             return;
138         }
139 
140         final DetailAST varNameAST = getVarNameAST(aAST);
141         String varName = varNameAST.getText();
142         boolean inInterfaceBlock = inInterfaceBlock(aAST);
143         final Set mods = getModifiers(aAST);
144         final String declaredScope = getVisibilityScope(mods);
145         final String variableScope =
146              inInterfaceBlock ? "public" : declaredScope;
147 
148         if (!("private".equals(variableScope)
149                 || inInterfaceBlock // implicitly static and final
150                 || mods.contains("static") && mods.contains("final")
151                 || "package".equals(variableScope) && isPackageAllowed()
152                 || "protected".equals(variableScope) && isProtectedAllowed()
153                 || "public".equals(variableScope)
154                    && getPublicMemberRegexp().match(varName)))
155         {
156             log(varNameAST.getLineNo(), varNameAST.getColumnNo(),
157                     "variable.notPrivate", varName);
158         }
159     }
160 
161     /**
162      * Returns the variable name in a VARIABLE_DEF AST.
163      * @param aVariableDefAST an AST where type == VARIABLE_DEF AST.
164      * @return the variable name in aVariableDefAST
165      */
166     private DetailAST getVarNameAST(DetailAST aVariableDefAST)
167     {
168         AST ast = aVariableDefAST.getFirstChild();
169         while (ast != null) {
170             AST nextSibling = ast.getNextSibling();
171             if (ast.getType() == TokenTypes.TYPE) {
172                 return (DetailAST) nextSibling;
173             }
174             ast = nextSibling;
175         }
176         return null;
177     }
178 
179     /**
180      * Returns whether an AST is in an interface block.
181      * @param aAST the AST to check for
182      * @return true iff aAST is in an interface def with no class def in between
183      */
184     private boolean inInterfaceBlock(DetailAST aAST)
185     {
186         DetailAST ast = aAST.getParent();
187         while (ast != null) {
188             switch (ast.getType()) {
189             case TokenTypes.INTERFACE_DEF:
190                 return true;
191             case TokenTypes.CLASS_DEF:
192                 return false;
193             default:
194                 ast = ast.getParent();
195             }
196         }
197         return false;
198     }
199 
200     /**
201      * Returns the set of modifier Strings for a VARIABLE_DEF AST.
202      * @param aVariableDefAST AST for a vraiable definition
203      * @return the set of modifier Strings for variableDefAST
204      */
205     private Set getModifiers(DetailAST aVariableDefAST)
206     {
207         final AST modifiersAST = aVariableDefAST.getFirstChild();
208         if (modifiersAST.getType() != TokenTypes.MODIFIERS) {
209             throw new IllegalStateException("Strange parse tree");
210         }
211         final Set retVal = new HashSet();
212         AST modifier = modifiersAST.getFirstChild();
213         while (modifier != null) {
214             retVal.add(modifier.getText());
215             modifier = modifier.getNextSibling();
216         }
217         return retVal;
218 
219     }
220 
221     /**
222      * Returns the visibility scope specified with a set of modifiers.
223      * @param aModifiers the set of modifier Strings
224      * @return one of "public", "private", "protected", "package"
225      */
226     private String getVisibilityScope(Set aModifiers)
227     {
228         final String[] explicitModifiers = {"public", "private", "protected"};
229         for (int i = 0; i < explicitModifiers.length; i++) {
230             String candidate = explicitModifiers[i];
231             if (aModifiers.contains(candidate)) {
232                 return candidate;
233             }
234         }
235         return "package";
236     }
237 }