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/IllegalInstantiationCheck.java


1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2002  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  
20  package com.puppycrawl.tools.checkstyle.checks;
21  
22  import java.util.HashSet;
23  import java.util.Iterator;
24  import java.util.Set;
25  import java.util.StringTokenizer;
26  
27  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
28  import com.puppycrawl.tools.checkstyle.api.DetailAST;
29  import com.puppycrawl.tools.checkstyle.api.FullIdent;
30  import antlr.collections.AST;
31  
32  // TODO: Clean up potential duplicate code here and in UnusedImportsCheck
33  /**
34   * <p>
35   * Checks for illegal instantiations where a factory method is preferred.
36   * </p>
37   * <p>
38   * Rationale: Depending on the project, for some classes it might be
39   * preferable to create instances through factory methods rather than
40   * calling the constructor.
41   * </p>
42   * <p>
43   * A simple example is the java.lang.Boolean class, to save memory and CPU
44   * cycles it is preferable to use the predeifined constants TRUE and FALSE.
45   * Constructor invocations should be replaced by calls to Boolean.valueOf().
46   * </p>
47   * <p>
48   * Some extremely performance sensitive projects may require the use of factory
49   * methods for other classes as well, to enforce the usage of number caches or
50   * object pools.
51   * </p>
52   * <p>
53   * Limitations: It is currently not possible to specify array classes.
54   * </p>
55   * <p>
56   * An example of how to configure the check is:
57   * </p>
58   * <pre>
59   * &lt;module name="IllegalInstantiation"/&gt;
60   * </pre>
61   * @author lkuehne
62   */
63  public class IllegalInstantiationCheck
64          extends AbstractImportCheck
65  {
66      /** Set of fully qualified classnames. E.g. "java.lang.Boolean" */
67      private final Set mIllegalClasses = new HashSet();
68  
69      /** name of the package */
70      private String mPkgName = null;
71  
72      /** the imports for the file */
73      private final Set mImports = new HashSet();
74  
75      /** @see com.puppycrawl.tools.checkstyle.api.Check */
76      public int[] getDefaultTokens()
77      {
78          return new int[] {
79              TokenTypes.IMPORT,
80              TokenTypes.LITERAL_NEW,
81              TokenTypes.PACKAGE_DEF
82          };
83      }
84      
85      /**
86       * Prevent user from changing tokens in the configuration.
87       * @see com.puppycrawl.tools.checkstyle.api.Check
88       */
89      public int[] getAcceptableTokens()
90      {
91          return new int[] {};
92      }
93      
94      /** @see com.puppycrawl.tools.checkstyle.api.Check */
95      public int[] getRequiredTokens()
96      {
97          return new int[] {
98              TokenTypes.IMPORT,
99              TokenTypes.LITERAL_NEW,
100             TokenTypes.PACKAGE_DEF
101         };
102     }
103     
104     /** @see com.puppycrawl.tools.checkstyle.api.Check */
105     public void beginTree()
106     {
107         super.beginTree();
108         mPkgName = null;
109         mImports.clear();
110     }
111 
112     /** @see com.puppycrawl.tools.checkstyle.api.Check */
113     public void visitToken(DetailAST aAST)
114     {
115         switch (aAST.getType()) {
116             case TokenTypes.LITERAL_NEW:
117                 processLiteralNew(aAST);
118                 break;
119             case TokenTypes.PACKAGE_DEF:
120                 processPackageDef(aAST);
121                 break;
122             case TokenTypes.IMPORT:
123                 processImport(aAST);
124                 break;
125         }
126     }
127 
128     /**
129      * Perform processing for an import token
130      * @param aAST the import token
131      */
132     private void processImport(DetailAST aAST)
133     {
134         final FullIdent name = getImportText(aAST);
135         if (name != null) {
136             // Note: different from UnusedImportsCheck.processImport(),
137             // '.*' imports are also added here
138             mImports.add(name);
139         }
140     }
141 
142     /**
143      * Perform processing for an package token
144      * @param aAST the package token
145      */
146     private void processPackageDef(DetailAST aAST)
147     {
148         final DetailAST packageNameAST = (DetailAST) aAST.getFirstChild();
149         final FullIdent packageIdent =
150                 FullIdent.createFullIdent(packageNameAST);
151         mPkgName = packageIdent.getText();
152     }
153 
154     /**
155      * Perform processing for an "new" token
156      * @param aAST the "new" token
157      */
158     private void processLiteralNew(DetailAST aAST)
159     {
160         final DetailAST typeNameAST = (DetailAST) aAST.getFirstChild();
161 
162         final AST nameSibling = typeNameAST.getNextSibling();
163         if (nameSibling != null
164                 && nameSibling.getType() == TokenTypes.ARRAY_DECLARATOR)
165         {
166             // aAST == "new Boolean[]"
167             return;
168         }
169 
170         FullIdent typeIdent = FullIdent.createFullIdent(typeNameAST);
171         final String typeName = typeIdent.getText();
172         final int lineNo = aAST.getLineNo();
173         final int colNo = aAST.getColumnNo();
174         final String fqClassName = getIllegalInstantiation(typeName);
175         if (fqClassName != null) {
176             log(lineNo, colNo, "instantiation.avoid", fqClassName);
177         }
178     }
179 
180     /**
181      * Checks illegal instantiations.
182      * @param aClassName instantiated class, may or may not be qualified
183      * @return the fully qualified class name of aClassName
184      * or null if instantiation of aClassName is OK
185      */
186     private String getIllegalInstantiation(String aClassName)
187     {
188         final String javaLang = "java.lang.";
189 
190         if (mIllegalClasses.contains(aClassName)) {
191             return aClassName;
192         }
193 
194         final int clsNameLen = aClassName.length();
195         final int pkgNameLen = (mPkgName == null) ? 0 : mPkgName.length();
196 
197         final Iterator illIter = mIllegalClasses.iterator();
198         while (illIter.hasNext()) {
199             final String illegal = (String) illIter.next();
200             final int illegalLen = illegal.length();
201 
202             // class from java.lang
203             if (((illegalLen - javaLang.length()) == clsNameLen)
204                 && illegal.endsWith(aClassName)
205                 && illegal.startsWith(javaLang))
206             {
207                 return illegal;
208             }
209 
210             // class from same package
211 
212             // the toplevel package (mPkgName == null) is covered by the
213             // "illegalInsts.contains(aClassName)" check above
214 
215             // the test is the "no garbage" version of
216             // illegal.equals(mPkgName + "." + aClassName)
217             if (mPkgName != null
218                 && clsNameLen == illegalLen - pkgNameLen - 1
219                 && illegal.charAt(pkgNameLen) == '.'
220                 && illegal.endsWith(aClassName)
221                 && illegal.startsWith(mPkgName))
222             {
223                 return illegal;
224             }
225             // import statements
226             final Iterator importIter = mImports.iterator();
227             while (importIter.hasNext()) {
228                 final FullIdent importLineText = (FullIdent) importIter.next();
229                 final String importArg = importLineText.getText();
230                 if (importArg.endsWith(".*")) {
231                     final String fqClass =
232                         importArg.substring(0, importArg.length() - 1)
233                         + aClassName;
234                     // assume that illegalInsts only contain existing classes
235                     // or else we might create a false alarm here
236                     if (mIllegalClasses.contains(fqClass)) {
237                         return fqClass;
238                     }
239                 }
240                 else {
241                     if (basename(importArg).equals(aClassName)
242                         && mIllegalClasses.contains(importArg))
243                     {
244                         return importArg;
245                     }
246                 }
247             }
248         }
249         return null;
250     }
251 
252     /**
253      * @return the class name from a fully qualified name
254      * @param aType the fully qualified name
255      */
256     private static String basename(String aType)
257     {
258         final int i = aType.lastIndexOf(".");
259         return (i == -1) ? aType : aType.substring(i + 1);
260     }
261 
262     /**
263      * Sets the classes that are illegal to instantiate.
264      * @param aClassNames a comma seperate list of class names
265      */
266     public void setClasses(String aClassNames)
267     {
268         mIllegalClasses.clear();
269         final StringTokenizer tok = new StringTokenizer(aClassNames, ",");
270         while (tok.hasMoreTokens()) {
271             mIllegalClasses.add(tok.nextToken());
272         }
273     }
274 }