Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ClassResolver |
|
| 6.2;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 | } |