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/AbstractTypeAwareCheck.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;
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.TokenTypes;
25  import com.puppycrawl.tools.checkstyle.api.Utils;
26  import java.util.HashSet;
27  import java.util.Set;
28  
29  /**
30   * Abstract class that endeavours to maintain type information for the Java
31   * file being checked. It provides helper methods for performing type
32   * information functions.
33   *
34   * @author Oliver Burn
35   * @version 1.0
36   */
37  public abstract class AbstractTypeAwareCheck
38      extends Check
39  {
40      /** imports details **/
41      private Set mImports = new HashSet();
42  
43      /** full identifier for package of the method **/
44      private FullIdent mPackageFullIdent;
45  
46      /** <code>ClassResolver</code> instance for current tree. */
47      private ClassResolver mClassResolver;
48  
49      /**
50       * Called to process an AST when visiting it.
51       * @param aAST the AST to process. Guaranteed to not be PACKAGE_DEF or
52       *             IMPORT tokens.
53       */
54      protected abstract void processAST(DetailAST aAST);
55  
56      /** @see com.puppycrawl.tools.checkstyle.api.Check */
57      public void beginTree(DetailAST aRootAST)
58      {
59          mPackageFullIdent = FullIdent.createFullIdent(null);
60          mImports.clear();
61          mClassResolver = null;
62      }
63  
64      /** @see com.puppycrawl.tools.checkstyle.api.Check */
65      public final void visitToken(DetailAST aAST)
66      {
67          if (aAST.getType() == TokenTypes.PACKAGE_DEF) {
68              processPackage(aAST);
69          }
70          else if (aAST.getType() == TokenTypes.IMPORT) {
71              processImport(aAST);
72          }
73          else {
74              processAST(aAST);
75          }
76      }
77  
78      /**
79       * Calculate if one type name is a shortname for another type name.
80       * @param aShortName a shorthand, such as <code>IOException</code>
81       * @param aFullName a full name, such as <code>java.io.IOException</code>
82       * @return true iff aShortName represents the same type as aFullName
83       */
84      protected boolean isShortName(String aShortName, String aFullName)
85      {
86          if (aShortName.length() >= aFullName.length()) {
87              return false;
88          }
89  
90          final String base = Utils.baseClassname(aFullName);
91          if (aShortName.length() >= aFullName.length()
92                  || !base.equals(aShortName))
93          {
94              return false;
95          }
96  
97          // check fully qualified import
98          if (mImports.contains(aFullName)) {
99              return true;
100         }
101 
102         // check .* import
103         final int endIndex = aFullName.length() - base.length() - 1;
104         final String packageName = aFullName.substring(0, endIndex);
105         final String starImport = packageName + ".*";
106         if (mImports.contains(starImport)) {
107             return true;
108         }
109 
110         // check fully qualified class from same package
111         return packageName.equals(mPackageFullIdent.getText());
112     }
113 
114     /**
115      * Is exception is unchecked (subclass of <code>RuntimeException</code>
116      * or <code>Error</code>
117      *
118      * @param aException <code>Class</code> of exception to check
119      * @return true  if exception is unchecked
120      *         false if exception is checked
121      */
122     protected boolean isUnchecked(Class aException)
123     {
124         return isSubclass(aException, RuntimeException.class)
125             || isSubclass(aException, Error.class);
126     }
127 
128     /**
129      * Checks if one class is subclass of another
130      *
131      * @param aChild <code>Class</code> of class
132      *               which should be child
133      * @param aParent <code>Class</code> of class
134      *                which should be parent
135      * @return true  if aChild is subclass of aParent
136      *         false otherwise
137      */
138     protected boolean isSubclass(Class aChild, Class aParent)
139     {
140         return (aParent != null) && (aChild != null)
141             &&  aParent.isAssignableFrom(aChild);
142     }
143 
144     /**
145      * Return if two Strings represent the same type, inspecting the
146      * import statements if necessary
147      *
148      * @param aFirst first type declared in throws clause
149      * @param aSecond second type declared in thros tag
150      * @return true iff type names represent the same type
151      */
152     protected boolean isSameType(String aFirst, String aSecond)
153     {
154         return aFirst.equals(aSecond)
155                 || isShortName(aFirst, aSecond)
156                 || isShortName(aSecond, aFirst);
157     }
158 
159     /** @return <code>ClassResolver</code> for current tree. */
160     private ClassResolver getClassResolver()
161     {
162         if (mClassResolver == null) {
163             mClassResolver =
164                 new ClassResolver(getClassLoader(),
165                                   mPackageFullIdent.getText(),
166                                   mImports);
167         }
168         return mClassResolver;
169     }
170 
171     /**
172      * Attempts to resolve the Class for a specified name.
173      * @param aClassName name of the class to resolve
174      * @return the resolved class or <code>null</code>
175      *          if unable to resolve the class.
176      */
177     protected final Class resolveClass(String aClassName)
178     {
179         try {
180             return getClassResolver().resolve(aClassName);
181         }
182         catch (ClassNotFoundException e) {
183             return null;
184         }
185     }
186 
187     /**
188      * Tries to load class. Logs error if unable.
189      * @param aIdent name of class which we try to load.
190      * @return <code>Class</code> for a ident.
191      */
192     protected final Class tryLoadClass(FullIdent aIdent)
193     {
194         Class clazz = resolveClass(aIdent.getText());
195         if (clazz == null) {
196             logLoadError(aIdent);
197         }
198         return clazz;
199     }
200 
201     /**
202      * Logs error if unable to load class information.
203      * Abstract, should be overrided in subclasses.
204      * @param aIdent class name for which we can no load class.
205      */
206     protected abstract void logLoadError(FullIdent aIdent);
207 
208     /**
209      * Collects the details of a package.
210      * @param aAST node containing the package details
211      */
212     private void processPackage(DetailAST aAST)
213     {
214         final DetailAST nameAST = (DetailAST) aAST.getFirstChild();
215         mPackageFullIdent = FullIdent.createFullIdent(nameAST);
216     }
217 
218     /**
219      * Collects the details of imports.
220      * @param aAST node containing the import details
221      */
222     private void processImport(DetailAST aAST)
223     {
224         final FullIdent name = FullIdent.createFullIdentBelow(aAST);
225         if (name != null) {
226             mImports.add(name.getText());
227         }
228     }
229 
230     /**
231      * Contains class's <code>FullIdent</code>
232      * and <code>Class</code> object if we can load it.
233      */
234     protected class ClassInfo
235     {
236         /** <code>FullIdent</code> associated with this class. */
237         private FullIdent mName;
238         /** <code>Class</code> object of this class if it's loadable. */
239         private Class mClass;
240         /** is class loadable. */
241         private boolean mIsLoadable;
242 
243         /**
244          * Creates new instance of of class information object.
245          * @param aName <code>FullIdent</code> associated with new object.
246          * @param aClass <code>Class</code> associated with new object
247          *               or null id class is not loadable.
248          */
249         public ClassInfo(FullIdent aName, Class aClass)
250         {
251             if (aName == null && aClass == null) {
252                 throw new NullPointerException(
253                     "ClassInfo's name or class should be non-null");
254             }
255             mName = aName;
256             setClazz(aClass);
257         }
258 
259         /**
260          * Creates new instance of of class information object.
261          * @param aName <code>FullIdent</code> associated with new object.
262          */
263         public ClassInfo(FullIdent aName)
264         {
265             if (aName == null) {
266                 throw new NullPointerException(
267                     "ClassInfo's name should be non-null");
268             }
269             mName = aName;
270             mIsLoadable = true;
271         }
272 
273         /** @return class name */
274         public final FullIdent getName()
275         {
276             return mName;
277         }
278 
279         /** @return if class is loadable ot not. */
280         public final boolean isLoadable()
281         {
282             return mIsLoadable;
283         }
284 
285         /** @return <code>Class</code> associated with an object. */
286         public final Class getClazz()
287         {
288             if (isLoadable() && mClass == null) {
289                 setClazz(AbstractTypeAwareCheck.this.tryLoadClass(getName()));
290             }
291             return mClass;
292         }
293 
294         /**
295          * Associates <code> Class</code> with an object.
296          * @param aClass <code>Class</code> to associate with.
297          */
298         public final void setClazz(Class aClass)
299         {
300             mClass = aClass;
301             mIsLoadable = (mClass != null);
302         }
303     }
304 }