Coverage Report - com.puppycrawl.tools.checkstyle.checks.ClassResolver
 
Classes in this File Line Coverage Branch Coverage Complexity
ClassResolver
95%
44/46
96%
29/30
6.2
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2014  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 java.util.Set;
 22  
 
 23  
 /**
 24  
  * Utility class to resolve a class name to an actual class. Note that loaded
 25  
  * classes are not initialized.
 26  
  * <p>Limitations: this does not handle inner classes very well.</p>
 27  
  *
 28  
  * @author Oliver Burn
 29  
  * @version 1.0
 30  
  */
 31  
 public class ClassResolver
 32  
 {
 33  
     /** name of the package to check if the class belongs to **/
 34  
     private final String mPkg;
 35  
     /** set of imports to check against **/
 36  
     private final Set<String> mImports;
 37  
     /** use to load classes **/
 38  
     private final ClassLoader mLoader;
 39  
 
 40  
     /**
 41  
      * Creates a new <code>ClassResolver</code> instance.
 42  
      *
 43  
      * @param aLoader the ClassLoader to load classes with.
 44  
      * @param aPkg the name of the package the class may belong to
 45  
      * @param aImports set of imports to check if the class belongs to
 46  
      */
 47  
     public ClassResolver(ClassLoader aLoader, String aPkg, Set<String> aImports)
 48  19
     {
 49  19
         mLoader = aLoader;
 50  19
         mPkg = aPkg;
 51  19
         mImports = aImports;
 52  19
         mImports.add("java.lang.*");
 53  19
     }
 54  
 
 55  
     /**
 56  
      * Attempts to resolve the Class for a specified name. The algorithm is
 57  
      * to check:
 58  
      * - fully qualified name
 59  
      * - explicit imports
 60  
      * - enclosing package
 61  
      * - star imports
 62  
      * @param aName name of the class to resolve
 63  
      * @param aCurrentClass name of current class (for inner classes).
 64  
      * @return the resolved class
 65  
      * @throws ClassNotFoundException if unable to resolve the class
 66  
      */
 67  
     public Class<?> resolve(String aName, String aCurrentClass)
 68  
         throws ClassNotFoundException
 69  
     {
 70  
         // See if the class is full qualified
 71  129
         Class<?> clazz = resolveQualifiedName(aName);
 72  129
         if (clazz != null) {
 73  42
             return clazz;
 74  
         }
 75  
 
 76  
         // try matching explicit imports
 77  87
         for (String imp : mImports) {
 78  
             // Very important to add the "." in the check below. Otherwise you
 79  
             // when checking for "DataException", it will match on
 80  
             // "SecurityDataException". This has been the cause of a very
 81  
             // difficult bug to resolve!
 82  170
             if (imp.endsWith("." + aName)) {
 83  26
                 clazz = resolveQualifiedName(imp);
 84  26
                 if (clazz != null) {
 85  26
                     return clazz;
 86  
                 }
 87  
 
 88  
             }
 89  
         }
 90  
 
 91  
         // See if in the package
 92  61
         if (!"".equals(mPkg)) {
 93  38
             clazz = resolveQualifiedName(mPkg + "." + aName);
 94  38
             if (clazz != null) {
 95  7
                 return clazz;
 96  
             }
 97  
         }
 98  
 
 99  
         //inner class of this class???
 100  54
         if (!"".equals(aCurrentClass)) {
 101  49
             final String innerClass = (!"".equals(mPkg) ? (mPkg + ".") : "")
 102  
                 + aCurrentClass + "$" + aName;
 103  49
             if (isLoadable(innerClass)) {
 104  9
                 return safeLoad(innerClass);
 105  
             }
 106  
         }
 107  
 
 108  
         // try star imports
 109  45
         for (String imp : mImports) {
 110  57
             if (imp.endsWith(".*")) {
 111  53
                 final String fqn = imp.substring(0, imp.lastIndexOf('.') + 1)
 112  
                     + aName;
 113  53
                 clazz = resolveQualifiedName(fqn);
 114  53
                 if (clazz != null) {
 115  42
                     return clazz;
 116  
                 }
 117  15
             }
 118  
         }
 119  
 
 120  
         // Giving up, the type is unknown, so load the class to generate an
 121  
         // exception
 122  3
         return safeLoad(aName);
 123  
     }
 124  
 
 125  
     /**
 126  
      * @return whether a specified class is loadable with safeLoad().
 127  
      * @param aName name of the class to check
 128  
      */
 129  
     public boolean isLoadable(String aName)
 130  
     {
 131  
         try {
 132  348
             safeLoad(aName);
 133  126
             return true;
 134  
         }
 135  222
         catch (final ClassNotFoundException e) {
 136  222
             return false;
 137  
         }
 138  
     }
 139  
 
 140  
     /**
 141  
      * Will load a specified class is such a way that it will NOT be
 142  
      * initialised.
 143  
      * @param aName name of the class to load
 144  
      * @return the <code>Class</code> for the specified class
 145  
      * @throws ClassNotFoundException if an error occurs
 146  
      */
 147  
     public Class<?> safeLoad(String aName)
 148  
         throws ClassNotFoundException
 149  
     {
 150  
         // The next line will load the class using the specified class
 151  
         // loader. The magic is having the "false" parameter. This means the
 152  
         // class will not be initialised. Very, very important.
 153  477
         return Class.forName(aName, false, mLoader);
 154  
     }
 155  
 
 156  
     /**
 157  
      * Tries to resolve a class for fully-specified name.
 158  
      * @param aName a given name of class.
 159  
      * @return Class object for the given name or null.
 160  
      */
 161  
     private Class<?> resolveQualifiedName(final String aName)
 162  
     {
 163  
         try {
 164  246
             if (isLoadable(aName)) {
 165  111
                 return safeLoad(aName);
 166  
             }
 167  
             //Perhaps it's fully-qualified inner class
 168  135
             final int dot = aName.lastIndexOf(".");
 169  135
             if (dot != -1) {
 170  53
                 final String innerName =
 171  
                     aName.substring(0, dot) + "$" + aName.substring(dot + 1);
 172  53
                 if (isLoadable(innerName)) {
 173  6
                     return safeLoad(innerName);
 174  
                 }
 175  
             }
 176  
         }
 177  0
         catch (final ClassNotFoundException ex) {
 178  
             // we shouldn't get this exception here,
 179  
             // so this is unexpected runtime exception
 180  0
             throw new RuntimeException(ex);
 181  129
         }
 182  
 
 183  129
         return null;
 184  
     }
 185  
 }