KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > puppycrawl > tools > checkstyle > checks > coding > IllegalInstantiationCheck


1 ////////////////////////////////////////////////////////////////////////////////
2
// checkstyle: Checks Java source code for adherence to a set of rules.
3
// Copyright (C) 2001-2005 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.coding;
20
21 import antlr.collections.AST;
22 import com.puppycrawl.tools.checkstyle.api.Check;
23 import com.puppycrawl.tools.checkstyle.api.DetailAST;
24 import com.puppycrawl.tools.checkstyle.api.FullIdent;
25 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26 import com.puppycrawl.tools.checkstyle.api.Utils;
27 import java.util.HashSet JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.Set JavaDoc;
30 import java.util.StringTokenizer JavaDoc;
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 Check
65 {
66     /** Set of fully qualified classnames. E.g. "java.lang.Boolean" */
67     private final Set JavaDoc mIllegalClasses = new HashSet JavaDoc();
68
69     /** name of the package */
70     private String JavaDoc mPkgName;
71
72     /** the imports for the file */
73     private final Set JavaDoc mImports = new HashSet JavaDoc();
74
75     /** the class names defined in the file */
76     private final Set JavaDoc mClassNames = new HashSet JavaDoc();
77
78     /** the instantiations in the file */
79     private final Set JavaDoc mInstantiations = new HashSet JavaDoc();
80
81     /** {@inheritDoc} */
82     public int[] getDefaultTokens()
83     {
84         return new int[] {
85             TokenTypes.IMPORT,
86             TokenTypes.LITERAL_NEW,
87             TokenTypes.PACKAGE_DEF,
88             TokenTypes.CLASS_DEF,
89         };
90     }
91
92     /**
93      * Prevent user from changing tokens in the configuration.
94      * @see com.puppycrawl.tools.checkstyle.api.Check
95      * @return empty array to not allow user to change configuration.
96      */

97     public int[] getAcceptableTokens()
98     {
99         return new int[] {};
100     }
101
102     /** {@inheritDoc} */
103     public int[] getRequiredTokens()
104     {
105         return new int[] {
106             TokenTypes.IMPORT,
107             TokenTypes.LITERAL_NEW,
108             TokenTypes.PACKAGE_DEF,
109         };
110     }
111
112     /** {@inheritDoc} */
113     public void beginTree(DetailAST aRootAST)
114     {
115         super.beginTree(aRootAST);
116         mPkgName = null;
117         mImports.clear();
118         mInstantiations.clear();
119         mClassNames.clear();
120     }
121
122     /** {@inheritDoc} */
123     public void visitToken(DetailAST aAST)
124     {
125         switch (aAST.getType()) {
126         case TokenTypes.LITERAL_NEW:
127             processLiteralNew(aAST);
128             break;
129         case TokenTypes.PACKAGE_DEF:
130             processPackageDef(aAST);
131             break;
132         case TokenTypes.IMPORT:
133             processImport(aAST);
134             break;
135         case TokenTypes.CLASS_DEF:
136             processClassDef(aAST);
137             break;
138         default:
139             throw new IllegalArgumentException JavaDoc("Unknown type " + aAST);
140         }
141     }
142
143     /**
144      * {@inheritDoc}
145      */

146     public void finishTree(DetailAST aRootAST)
147     {
148         for (final Iterator JavaDoc it = mInstantiations.iterator(); it.hasNext();) {
149             final DetailAST literalNewAST = (DetailAST) it.next();
150             postprocessLiteralNew(literalNewAST);
151         }
152     }
153
154     /**
155      * Collects classes defined in the source file. Required
156      * to avoid false alarms for local vs. java.lang classes.
157      *
158      * @param aAST the classdef token.
159      */

160     private void processClassDef(DetailAST aAST)
161     {
162         final DetailAST identToken = aAST.findFirstToken(TokenTypes.IDENT);
163         final String JavaDoc className = identToken.getText();
164         mClassNames.add(className);
165     }
166
167     /**
168      * Perform processing for an import token
169      * @param aAST the import token
170      */

171     private void processImport(DetailAST aAST)
172     {
173         final FullIdent name = FullIdent.createFullIdentBelow(aAST);
174         if (name != null) {
175             // Note: different from UnusedImportsCheck.processImport(),
176
// '.*' imports are also added here
177
mImports.add(name);
178         }
179     }
180
181     /**
182      * Perform processing for an package token
183      * @param aAST the package token
184      */

185     private void processPackageDef(DetailAST aAST)
186     {
187         final DetailAST packageNameAST = aAST.getLastChild()
188                 .getPreviousSibling();
189         final FullIdent packageIdent =
190                 FullIdent.createFullIdent(packageNameAST);
191         mPkgName = packageIdent.getText();
192     }
193
194     /**
195      * Collects a "new" token.
196      * @param aAST the "new" token
197      */

198     private void processLiteralNew(DetailAST aAST)
199     {
200         mInstantiations.add(aAST);
201     }
202
203     /**
204      * Processes one of the collected "new" tokens when treewalking
205      * has finished.
206      * @param aAST the "new" token.
207      */

208     private void postprocessLiteralNew(DetailAST aAST)
209     {
210         final DetailAST typeNameAST = (DetailAST) aAST.getFirstChild();
211         final AST nameSibling = typeNameAST.getNextSibling();
212         if ((nameSibling != null)
213                 && (nameSibling.getType() == TokenTypes.ARRAY_DECLARATOR))
214         {
215             // aAST == "new Boolean[]"
216
return;
217         }
218
219         final FullIdent typeIdent = FullIdent.createFullIdent(typeNameAST);
220         final String JavaDoc typeName = typeIdent.getText();
221         final int lineNo = aAST.getLineNo();
222         final int colNo = aAST.getColumnNo();
223         final String JavaDoc fqClassName = getIllegalInstantiation(typeName);
224         if (fqClassName != null) {
225             log(lineNo, colNo, "instantiation.avoid", fqClassName);
226         }
227     }
228
229     /**
230      * Checks illegal instantiations.
231      * @param aClassName instantiated class, may or may not be qualified
232      * @return the fully qualified class name of aClassName
233      * or null if instantiation of aClassName is OK
234      */

235     private String JavaDoc getIllegalInstantiation(String JavaDoc aClassName)
236     {
237         final String JavaDoc javaLang = "java.lang.";
238
239         if (mIllegalClasses.contains(aClassName)) {
240             return aClassName;
241         }
242
243         final int clsNameLen = aClassName.length();
244         final int pkgNameLen = (mPkgName == null) ? 0 : mPkgName.length();
245
246         final Iterator JavaDoc illIter = mIllegalClasses.iterator();
247         while (illIter.hasNext()) {
248             final String JavaDoc illegal = (String JavaDoc) illIter.next();
249             final int illegalLen = illegal.length();
250
251             // class from java.lang
252
if (((illegalLen - javaLang.length()) == clsNameLen)
253                 && illegal.endsWith(aClassName)
254                 && illegal.startsWith(javaLang))
255             {
256                 // java.lang needs no import, but a class without import might
257
// also come from the same file or be in the same package.
258
// E.g. if a class defines an inner class "Boolean",
259
// the expression "new Boolean()" refers to that class,
260
// not to java.lang.Boolean
261

262                 final boolean isSameFile = mClassNames.contains(aClassName);
263
264                 boolean isSamePackage = false;
265                 try {
266                     final ClassLoader JavaDoc classLoader = getClassLoader();
267                     if (classLoader != null) {
268                         final String JavaDoc fqName = mPkgName + "." + aClassName;
269                         classLoader.loadClass(fqName);
270                         // no ClassNotFoundException, fqName is a known class
271
isSamePackage = true;
272                     }
273                 }
274                 catch (final ClassNotFoundException JavaDoc ex) {
275                     // not a class from the same package
276
isSamePackage = false;
277                 }
278
279                 if (!(isSameFile || isSamePackage)) {
280                     return illegal;
281                 }
282             }
283
284             // class from same package
285

286             // the toplevel package (mPkgName == null) is covered by the
287
// "illegalInsts.contains(aClassName)" check above
288

289             // the test is the "no garbage" version of
290
// illegal.equals(mPkgName + "." + aClassName)
291
if ((mPkgName != null)
292                 && (clsNameLen == illegalLen - pkgNameLen - 1)
293                 && (illegal.charAt(pkgNameLen) == '.')
294                 && illegal.endsWith(aClassName)
295                 && illegal.startsWith(mPkgName))
296             {
297                 return illegal;
298             }
299             // import statements
300
final Iterator JavaDoc importIter = mImports.iterator();
301             while (importIter.hasNext()) {
302                 final FullIdent importLineText = (FullIdent) importIter.next();
303                 final String JavaDoc importArg = importLineText.getText();
304                 if (importArg.endsWith(".*")) {
305                     final String JavaDoc fqClass =
306                         importArg.substring(0, importArg.length() - 1)
307                         + aClassName;
308                     // assume that illegalInsts only contain existing classes
309
// or else we might create a false alarm here
310
if (mIllegalClasses.contains(fqClass)) {
311                         return fqClass;
312                     }
313                 }
314                 else {
315                     if (Utils.baseClassname(importArg).equals(aClassName)
316                         && mIllegalClasses.contains(importArg))
317                     {
318                         return importArg;
319                     }
320                 }
321             }
322         }
323         return null;
324     }
325
326     /**
327      * Sets the classes that are illegal to instantiate.
328      * @param aClassNames a comma seperate list of class names
329      */

330     public void setClasses(String JavaDoc aClassNames)
331     {
332         mIllegalClasses.clear();
333         final StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(aClassNames, ",");
334         while (tok.hasMoreTokens()) {
335             mIllegalClasses.add(tok.nextToken());
336         }
337     }
338 }
339
Popular Tags