KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > puppycrawl > tools > checkstyle > checks > AbstractTypeAwareCheck


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;
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.LocalizedMessage;
25 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26 import java.util.HashSet JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.util.Set JavaDoc;
30 import java.util.Vector JavaDoc;
31
32 /**
33  * Abstract class that endeavours to maintain type information for the Java
34  * file being checked. It provides helper methods for performing type
35  * information functions.
36  *
37  * @author Oliver Burn
38  * @version 1.0
39  */

40 public abstract class AbstractTypeAwareCheck extends Check
41 {
42     /** imports details **/
43     private final Set JavaDoc mImports = new HashSet JavaDoc();
44
45     /** full identifier for package of the method **/
46     private FullIdent mPackageFullIdent;
47
48     /** Name of current class. */
49     private String JavaDoc mCurrentClass;
50
51     /** <code>ClassResolver</code> instance for current tree. */
52     private ClassResolver mClassResolver;
53
54     /** Stack of maps for type params. */
55     private final Vector JavaDoc mTypeParams = new Vector JavaDoc();
56
57     /**
58      * Whether to log class loading errors to the checkstyle report
59      * instead of throwing a RTE.
60      *
61      * Logging errors will avoid stopping checkstyle completely
62      * because of a typo in javadoc. However, with modern IDEs that
63      * support automated refactoring and generate javadoc this will
64      * occur rarely, so by default we assume a configuration problem
65      * in the checkstyle classpath and throw an execption.
66      *
67      * This configuration option was triggered by bug 1422462.
68      */

69     private boolean mLogLoadErrors;
70
71     /**
72      * Controls whether to log class loading errors to the checkstyle report
73      * instead of throwing a RTE.
74      *
75      * @param aLogLoadErrors true if errors should be logged
76      */

77     public final void setLogLoadErrors(boolean aLogLoadErrors)
78     {
79         mLogLoadErrors = aLogLoadErrors;
80     }
81
82     /**
83      * Whether to show class loading errors in the checkstyle report.
84      * Request ID 1491630
85      */

86     private boolean mSuppressLoadErrors;
87
88     /**
89      * Controls whether to show class loading errors in the checkstyle report.
90      *
91      * @param aSuppressLoadErrors true if errors shouldn't be shown
92      */

93     public final void setSuppressLoadErrors(boolean aSuppressLoadErrors)
94     {
95         mSuppressLoadErrors = aSuppressLoadErrors;
96     }
97
98     /**
99      * Called to process an AST when visiting it.
100      * @param aAST the AST to process. Guaranteed to not be PACKAGE_DEF or
101      * IMPORT tokens.
102      */

103     protected abstract void processAST(DetailAST aAST);
104
105     /** {@inheritDoc} */
106     public final int[] getRequiredTokens()
107     {
108         return new int[] {
109             TokenTypes.PACKAGE_DEF,
110             TokenTypes.IMPORT,
111             TokenTypes.CLASS_DEF,
112             TokenTypes.ENUM_DEF,
113         };
114     }
115
116     /** {@inheritDoc} */
117     public void beginTree(DetailAST aRootAST)
118     {
119         mPackageFullIdent = FullIdent.createFullIdent(null);
120         mImports.clear();
121         // add java.lang.* since it's always imported
122
mImports.add("java.lang.*");
123         mClassResolver = null;
124         mCurrentClass = "";
125         mTypeParams.clear();
126     }
127
128     /** {@inheritDoc} */
129     public final void visitToken(DetailAST aAST)
130     {
131         if (aAST.getType() == TokenTypes.PACKAGE_DEF) {
132             processPackage(aAST);
133         }
134         else if (aAST.getType() == TokenTypes.IMPORT) {
135             processImport(aAST);
136         }
137         else if ((aAST.getType() == TokenTypes.CLASS_DEF)
138                  || (aAST.getType() == TokenTypes.ENUM_DEF))
139         {
140             processClass(aAST);
141         }
142         else {
143             if (aAST.getType() == TokenTypes.METHOD_DEF) {
144                 processTypeParams(aAST);
145             }
146             processAST(aAST);
147         }
148     }
149
150     /** {@inheritDoc} */
151     public final void leaveToken(DetailAST aAST)
152     {
153         if ((aAST.getType() == TokenTypes.CLASS_DEF)
154             || (aAST.getType() == TokenTypes.ENUM_DEF))
155         {
156             // perhaps it was inner class
157
int dotIdx = mCurrentClass.lastIndexOf("$");
158             if (dotIdx == -1) {
159                 // perhaps just a class
160
dotIdx = mCurrentClass.lastIndexOf(".");
161             }
162             if (dotIdx == -1) {
163                 // looks like a topmost class
164
mCurrentClass = "";
165             }
166             else {
167                 mCurrentClass = mCurrentClass.substring(0, dotIdx);
168             }
169             mTypeParams.remove(mTypeParams.size() - 1);
170         }
171         else if (aAST.getType() == TokenTypes.METHOD_DEF) {
172             mTypeParams.remove(mTypeParams.size() - 1);
173         }
174         else if ((aAST.getType() != TokenTypes.PACKAGE_DEF)
175                  && (aAST.getType() != TokenTypes.IMPORT))
176         {
177             leaveAST(aAST);
178         }
179     }
180
181     /**
182      * Called when exiting an AST. A no-op by default, extending classes
183      * may choose to override this to augment their processing.
184      * @param aAST the AST we are departing. Guaranteed to not be PACKAGE_DEF,
185      * CLASS_DEF, or IMPORT
186      */

187     protected void leaveAST(DetailAST aAST)
188     {
189     }
190
191     /**
192      * Is exception is unchecked (subclass of <code>RuntimeException</code>
193      * or <code>Error</code>
194      *
195      * @param aException <code>Class</code> of exception to check
196      * @return true if exception is unchecked
197      * false if exception is checked
198      */

199     protected boolean isUnchecked(Class JavaDoc aException)
200     {
201         return isSubclass(aException, RuntimeException JavaDoc.class)
202             || isSubclass(aException, Error JavaDoc.class);
203     }
204
205     /**
206      * Checks if one class is subclass of another
207      *
208      * @param aChild <code>Class</code> of class
209      * which should be child
210      * @param aParent <code>Class</code> of class
211      * which should be parent
212      * @return true if aChild is subclass of aParent
213      * false otherwise
214      */

215     protected boolean isSubclass(Class JavaDoc aChild, Class JavaDoc aParent)
216     {
217         return (aParent != null) && (aChild != null)
218             && aParent.isAssignableFrom(aChild);
219     }
220
221     /** @return <code>ClassResolver</code> for current tree. */
222     private ClassResolver getClassResolver()
223     {
224         if (mClassResolver == null) {
225             mClassResolver =
226                 new ClassResolver(getClassLoader(),
227                                   mPackageFullIdent.getText(),
228                                   mImports);
229         }
230         return mClassResolver;
231     }
232
233     /**
234      * Attempts to resolve the Class for a specified name.
235      * @param aClassName name of the class to resolve
236      * @param aCurrentClass name of surrounding class.
237      * @return the resolved class or <code>null</code>
238      * if unable to resolve the class.
239      */

240     protected final Class JavaDoc resolveClass(String JavaDoc aClassName, String JavaDoc aCurrentClass)
241     {
242         try {
243             return getClassResolver().resolve(aClassName, aCurrentClass);
244         }
245         catch (final ClassNotFoundException JavaDoc e) {
246             return null;
247         }
248     }
249
250     /**
251      * Tries to load class. Logs error if unable.
252      * @param aIdent name of class which we try to load.
253      * @param aCurrentClass name of surrounding class.
254      * @return <code>Class</code> for a ident.
255      */

256     protected final Class JavaDoc tryLoadClass(Token aIdent, String JavaDoc aCurrentClass)
257     {
258         final Class JavaDoc clazz = resolveClass(aIdent.getText(), aCurrentClass);
259         if (clazz == null) {
260             logLoadError(aIdent);
261         }
262         return clazz;
263     }
264
265     /**
266      * Logs error if unable to load class information.
267      * Abstract, should be overrided in subclasses.
268      * @param aIdent class name for which we can no load class.
269      */

270     protected abstract void logLoadError(Token aIdent);
271
272     /**
273      * Common implementation for logLoadError() method.
274      * @param aLineNo line number of the problem.
275      * @param aColumnNo column number of the problem.
276      * @param aMsgKey message key to use.
277      * @param aValues values to fill the message out.
278      */

279     protected final void logLoadErrorImpl(int aLineNo, int aColumnNo,
280                                           String JavaDoc aMsgKey, Object JavaDoc[] aValues)
281     {
282         if (!mLogLoadErrors) {
283             final LocalizedMessage msg = new LocalizedMessage(aLineNo,
284                                                     aColumnNo,
285                                                     getMessageBundle(),
286                                                     aMsgKey,
287                                                     aValues,
288                                                     getSeverityLevel(),
289                                                     getId(),
290                                                     this.getClass());
291             throw new RuntimeException JavaDoc(msg.getMessage());
292         }
293
294         if (!mSuppressLoadErrors) {
295             log(aLineNo, aColumnNo, aMsgKey, aValues);
296         }
297     }
298
299     /**
300      * Collects the details of a package.
301      * @param aAST node containing the package details
302      */

303     private void processPackage(DetailAST aAST)
304     {
305         final DetailAST nameAST = aAST.getLastChild().getPreviousSibling();
306         mPackageFullIdent = FullIdent.createFullIdent(nameAST);
307     }
308
309     /**
310      * Collects the details of imports.
311      * @param aAST node containing the import details
312      */

313     private void processImport(DetailAST aAST)
314     {
315         final FullIdent name = FullIdent.createFullIdentBelow(aAST);
316         if (name != null) {
317             mImports.add(name.getText());
318         }
319     }
320
321     /**
322      * Process type params (if any) for given class, enum or method.
323      * @param aAST class, enum or method to process.
324      */

325     private void processTypeParams(DetailAST aAST)
326     {
327         final DetailAST typeParams =
328             aAST.findFirstToken(TokenTypes.TYPE_PARAMETERS);
329
330         final Map JavaDoc paramsMap = new HashMap JavaDoc();
331         mTypeParams.add(paramsMap);
332
333         if (typeParams == null) {
334             return;
335         }
336
337         for (DetailAST child = (DetailAST) typeParams.getFirstChild();
338              child != null;
339              child = (DetailAST) child.getNextSibling())
340         {
341             if (child.getType() == TokenTypes.TYPE_PARAMETER) {
342                 final DetailAST param = child;
343                 final String JavaDoc alias =
344                     param.findFirstToken(TokenTypes.IDENT).getText();
345                 final DetailAST bounds =
346                     param.findFirstToken(TokenTypes.TYPE_UPPER_BOUNDS);
347                 if (bounds != null) {
348                     final FullIdent name =
349                         FullIdent.createFullIdentBelow(bounds);
350                     final ClassInfo ci =
351                         createClassInfo(new Token(name), getCurrentClassName());
352                     paramsMap.put(alias, ci);
353                 }
354             }
355         }
356     }
357
358     /**
359      * Processes class definition.
360      * @param aAST class definition to process.
361      */

362     private void processClass(DetailAST aAST)
363     {
364         final DetailAST ident = aAST.findFirstToken(TokenTypes.IDENT);
365         mCurrentClass += ("".equals(mCurrentClass) ? "" : "$")
366             + ident.getText();
367
368         processTypeParams(aAST);
369     }
370
371     /**
372      * Returns current class.
373      * @return name of current class.
374      */

375     protected final String JavaDoc getCurrentClassName()
376     {
377         return mCurrentClass;
378     }
379
380     /**
381      * Creates class info for given name.
382      * @param aName name of type.
383      * @param aSurroundingClass name of surrounding class.
384      * @return class infor for given name.
385      */

386     protected final ClassInfo createClassInfo(final Token aName,
387                                               final String JavaDoc aSurroundingClass)
388     {
389         final ClassInfo ci = findClassAlias(aName.getText());
390         if (ci != null) {
391             return new ClassAlias(aName, ci);
392         }
393         return new RegularClass(aName, aSurroundingClass, this);
394     }
395
396     /**
397      * Looking if a given name is alias.
398      * @param aName given name
399      * @return ClassInfo for alias if it exists, null otherwise
400      */

401     protected final ClassInfo findClassAlias(final String JavaDoc aName)
402     {
403         ClassInfo ci = null;
404         for (int i = mTypeParams.size() - 1; i >= 0; i--) {
405             final Map JavaDoc paramMap = (Map JavaDoc) mTypeParams.get(i);
406             ci = (ClassInfo) paramMap.get(aName);
407             if (ci != null) {
408                 break;
409             }
410         }
411         return ci;
412     }
413
414     /**
415      * Contains class's <code>Token</code>.
416      */

417     protected abstract static class ClassInfo
418     {
419         /** <code>FullIdent</code> associated with this class. */
420         private final Token mName;
421
422         /** @return class name */
423         public final Token getName()
424         {
425             return mName;
426         }
427
428         /** @return <code>Class</code> associated with an object. */
429         public abstract Class JavaDoc getClazz();
430
431         /**
432          * Creates new instance of class inforamtion object.
433          * @param aName token which represents class name.
434          */

435         protected ClassInfo(final Token aName)
436         {
437             if (aName == null) {
438                 throw new NullPointerException JavaDoc(
439                     "ClassInfo's name should be non-null");
440             }
441             mName = aName;
442         }
443     }
444
445     /** Represents regular classes/enumes. */
446     private static final class RegularClass extends ClassInfo
447     {
448         /** name of surrounding class. */
449         private String JavaDoc mSurroundingClass;
450         /** is class loadable. */
451         private boolean mIsLoadable = true;
452         /** <code>Class</code> object of this class if it's loadable. */
453         private Class JavaDoc mClass;
454         /** the check we use to resolve classes. */
455         private final AbstractTypeAwareCheck mCheck;
456
457         /**
458          * Creates new instance of of class information object.
459          * @param aName <code>FullIdent</code> associated with new object.
460          * @param aSurroundingClass name of current surrounding class.
461          * @param aCheck the check we use to load class.
462          */

463         private RegularClass(final Token aName,
464                              final String JavaDoc aSurroundingClass,
465                              final AbstractTypeAwareCheck aCheck)
466         {
467             super(aName);
468             mSurroundingClass = aSurroundingClass;
469             mCheck = aCheck;
470         }
471         /** @return if class is loadable ot not. */
472         private boolean isLoadable()
473         {
474             return mIsLoadable;
475         }
476
477         /** @return <code>Class</code> associated with an object. */
478         public Class JavaDoc getClazz()
479         {
480             if (isLoadable() && (mClass == null)) {
481                 setClazz(mCheck.tryLoadClass(getName(), mSurroundingClass));
482             }
483             return mClass;
484         }
485
486         /**
487          * Associates <code> Class</code> with an object.
488          * @param aClass <code>Class</code> to associate with.
489          */

490         private void setClazz(Class JavaDoc aClass)
491         {
492             mClass = aClass;
493             mIsLoadable = (mClass != null);
494         }
495
496         /** {@inheritDoc} */
497         public String JavaDoc toString()
498         {
499             return "RegularClass[name=" + getName()
500                 + ", in class=" + mSurroundingClass
501                 + ", loadable=" + mIsLoadable
502                 + ", class=" + mClass + "]";
503         }
504     }
505
506     /** Represents type param which is "alias" for real type. */
507     private static class ClassAlias extends ClassInfo
508     {
509         /** Class information associated with the alias. */
510         private final ClassInfo mClassInfo;
511
512         /**
513          * Creates nnew instance of the class.
514          * @param aName token which represents name of class alias.
515          * @param aClassInfo class information associated with the alias.
516          */

517         ClassAlias(final Token aName, ClassInfo aClassInfo)
518         {
519             super(aName);
520             mClassInfo = aClassInfo;
521         }
522
523         /** {@inheritDoc} */
524         public final Class JavaDoc getClazz()
525         {
526             return mClassInfo.getClazz();
527         }
528
529         /** {@inheritDoc} */
530         public String JavaDoc toString()
531         {
532             return "ClassAlias[alias " + getName()
533                 + " for " + mClassInfo + "]";
534         }
535     }
536
537     /**
538      * Represents text element with location in the text.
539      */

540     protected static class Token
541     {
542         /** token's column number. */
543         private final int mColumn;
544         /** token's line number. */
545         private final int mLine;
546         /** token's text. */
547         private final String JavaDoc mText;
548
549         /**
550          * Creates token.
551          * @param aText token's text
552          * @param aLine token's line number
553          * @param aColumn token's column number
554          */

555         public Token(String JavaDoc aText, int aLine, int aColumn)
556         {
557             mText = aText;
558             mLine = aLine;
559             mColumn = aColumn;
560         }
561
562         /**
563          * Converts FullIdent to Token.
564          * @param aFullIdent full ident to convert.
565          */

566         public Token(FullIdent aFullIdent)
567         {
568             mText = aFullIdent.getText();
569             mLine = aFullIdent.getLineNo();
570             mColumn = aFullIdent.getColumnNo();
571         }
572
573         /** @return line number of the token */
574         public int getLineNo()
575         {
576             return mLine;
577         }
578
579         /** @return column number of the token */
580         public int getColumnNo()
581         {
582             return mColumn;
583         }
584
585         /** @return text of the token */
586         public String JavaDoc getText()
587         {
588             return mText;
589         }
590
591         /** {@inheritDoc} */
592         public String JavaDoc toString()
593         {
594             return "Token[" + getText() + "(" + getLineNo()
595                 + "x" + getColumnNo() + ")]";
596         }
597     }
598 }
599
Popular Tags