001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks; 020 021import java.util.Set; 022 023/** 024 * Utility class to resolve a class name to an actual class. Note that loaded 025 * classes are not initialized. 026 * <p>Limitations: this does not handle inner classes very well.</p> 027 * 028 * @author Oliver Burn 029 * @version 1.0 030 */ 031public class ClassResolver 032{ 033 /** name of the package to check if the class belongs to **/ 034 private final String pkg; 035 /** set of imports to check against **/ 036 private final Set<String> imports; 037 /** use to load classes **/ 038 private final ClassLoader loader; 039 040 /** 041 * Creates a new <code>ClassResolver</code> instance. 042 * 043 * @param loader the ClassLoader to load classes with. 044 * @param pkg the name of the package the class may belong to 045 * @param imports set of imports to check if the class belongs to 046 */ 047 public ClassResolver(ClassLoader loader, String pkg, Set<String> imports) 048 { 049 this.loader = loader; 050 this.pkg = pkg; 051 this.imports = imports; 052 imports.add("java.lang.*"); 053 } 054 055 /** 056 * Attempts to resolve the Class for a specified name. The algorithm is 057 * to check: 058 * - fully qualified name 059 * - explicit imports 060 * - enclosing package 061 * - star imports 062 * @param name name of the class to resolve 063 * @param currentClass name of current class (for inner classes). 064 * @return the resolved class 065 * @throws ClassNotFoundException if unable to resolve the class 066 */ 067 public Class<?> resolve(String name, String currentClass) 068 throws ClassNotFoundException 069 { 070 // See if the class is full qualified 071 Class<?> clazz = resolveQualifiedName(name); 072 if (clazz != null) { 073 return clazz; 074 } 075 076 // try matching explicit imports 077 for (String imp : imports) { 078 // Very important to add the "." in the check below. Otherwise you 079 // when checking for "DataException", it will match on 080 // "SecurityDataException". This has been the cause of a very 081 // difficult bug to resolve! 082 if (imp.endsWith("." + name)) { 083 clazz = resolveQualifiedName(imp); 084 if (clazz != null) { 085 return clazz; 086 } 087 088 } 089 } 090 091 // See if in the package 092 if (!"".equals(pkg)) { 093 clazz = resolveQualifiedName(pkg + "." + name); 094 if (clazz != null) { 095 return clazz; 096 } 097 } 098 099 //inner class of this class??? 100 if (!"".equals(currentClass)) { 101 final String innerClass = (!"".equals(pkg) ? (pkg + ".") : "") 102 + currentClass + "$" + name; 103 if (isLoadable(innerClass)) { 104 return safeLoad(innerClass); 105 } 106 } 107 108 // try star imports 109 for (String imp : imports) { 110 if (imp.endsWith(".*")) { 111 final String fqn = imp.substring(0, imp.lastIndexOf('.') + 1) 112 + name; 113 clazz = resolveQualifiedName(fqn); 114 if (clazz != null) { 115 return clazz; 116 } 117 } 118 } 119 120 // Giving up, the type is unknown, so load the class to generate an 121 // exception 122 return safeLoad(name); 123 } 124 125 /** 126 * @return whether a specified class is loadable with safeLoad(). 127 * @param name name of the class to check 128 */ 129 public boolean isLoadable(String name) 130 { 131 try { 132 safeLoad(name); 133 return true; 134 } 135 catch (final ClassNotFoundException e) { 136 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 name 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 name) 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 return Class.forName(name, false, loader); 154 } 155 156 /** 157 * Tries to resolve a class for fully-specified name. 158 * @param name a given name of class. 159 * @return Class object for the given name or null. 160 */ 161 private Class<?> resolveQualifiedName(final String name) 162 { 163 try { 164 if (isLoadable(name)) { 165 return safeLoad(name); 166 } 167 //Perhaps it's fully-qualified inner class 168 final int dot = name.lastIndexOf("."); 169 if (dot != -1) { 170 final String innerName = 171 name.substring(0, dot) + "$" + name.substring(dot + 1); 172 if (isLoadable(innerName)) { 173 return safeLoad(innerName); 174 } 175 } 176 } 177 catch (final ClassNotFoundException ex) { 178 // we shouldn't get this exception here, 179 // so this is unexpected runtime exception 180 throw new RuntimeException(ex); 181 } 182 183 return null; 184 } 185}