1 package org.andromda.core.common;
2
3 import java.lang.reflect.Field;
4 import java.lang.reflect.Method;
5 import java.lang.reflect.Modifier;
6 import java.net.URL;
7 import java.util.ArrayList;
8 import java.util.Arrays;
9 import java.util.Collection;
10 import java.util.LinkedHashSet;
11 import java.util.List;
12 import java.util.Set;
13 import org.apache.commons.collections.CollectionUtils;
14 import org.apache.commons.lang.StringUtils;
15
16 // TODO: FindBugs: This class has a simple name that is identical to that of its superclass. This can be exceptionally confusing.
17 /**
18 * Contains utilities for dealing with classes.
19 *
20 * @author Chad Brandon
21 * @author Bob Fields
22 */
23 public class ClassUtils
24 extends org.apache.commons.lang.ClassUtils
25 {
26 /**
27 * Creates a new instance of the class having the given <code>className</code>.
28 *
29 * @param className the name of the class to instantiate.
30 * @return Object the new instance
31 */
32 public static Object newInstance(final String className)
33 {
34 try
35 {
36 return loadClass(className).newInstance();
37 }
38 catch (final Throwable throwable)
39 {
40 throw new ClassUtilsException(throwable);
41 }
42 }
43
44 /**
45 * Creates a new instance of the class given the <code>type</code>.
46 *
47 * @param type the type from which to instantiate the new instance.
48 * @return Object the new instance
49 */
50 public static Object newInstance(final Class type)
51 {
52 try
53 {
54 return type != null ? type.newInstance() : null;
55 }
56 catch (final Throwable throwable)
57 {
58 throw new ClassUtilsException(throwable);
59 }
60 }
61
62 /**
63 * Loads and returns the class having the className. Will load but normal classes and the classes representing
64 * primitives.
65 *
66 * @param className the name of the class to load.
67 * @return Class the loaded class
68 */
69 public static Class loadClass(String className)
70 {
71 ExceptionUtils.checkEmpty(
72 "className",
73 className);
74 className = StringUtils.trimToNull(className);
75
76 // get rid of any array notation
77 className = StringUtils.replace(
78 className,
79 "[]",
80 "");
81
82 final ClassLoader loader = getClassLoader();
83 Class loadedClass;
84 try
85 {
86 // check and see if it's a primitive and if so convert it
87 if (ClassUtils.isPrimitiveType(className))
88 {
89 loadedClass = getPrimitiveClass(
90 className,
91 loader);
92 }
93 else
94 {
95 loadedClass = loader.loadClass(className);
96 }
97 }
98 catch (final Throwable throwable)
99 {
100 throw new ClassUtilsException(throwable);
101 }
102 return loadedClass;
103 }
104
105 /**
106 * Gets the appropriate class loader instance.
107 *
108 * @return the class loader.
109 */
110 public static ClassLoader getClassLoader()
111 {
112 ClassLoader loader = Thread.currentThread().getContextClassLoader();
113 if (loader == null)
114 {
115 loader = ClassUtils.class.getClassLoader();
116 Thread.currentThread().setContextClassLoader(loader);
117 }
118 return loader;
119 }
120
121 /**
122 * <p> Returns the type class name for a Java primitive.
123 * </p>
124 *
125 * @param name a <code>String</code> with the name of the type
126 * @param loader the loader to use.
127 * @return a <code>String</code> with the name of the corresponding
128 * java.lang wrapper class if <code>name</code> is a Java
129 * primitive type; <code>false</code> if not
130 */
131 protected static Class getPrimitiveClass(
132 final String name,
133 final ClassLoader loader)
134 {
135 ExceptionUtils.checkEmpty(
136 "name",
137 name);
138 ExceptionUtils.checkNull(
139 "loader",
140 loader);
141
142 Class primitiveClass = null;
143 if (isPrimitiveType(name) && !"void".equals(name))
144 {
145 final String className;
146 if ("char".equals(name))
147 {
148 className = "Character";
149 }
150 else if ("int".equals(name))
151 {
152 className = "Integer";
153 }
154 else
155 {
156 className = StringUtils.capitalize(name);
157 }
158
159 try
160 {
161 if (StringUtils.isNotBlank(className))
162 {
163 Field field = loader.loadClass(className).getField("TYPE");
164 primitiveClass = (Class)field.get(null);
165 }
166 }
167 catch (final Exception exception)
168 {
169 throw new ClassUtilsException(exception);
170 }
171 }
172 return primitiveClass;
173 }
174
175 /**
176 * Returns a collection of all static fields values for the given
177 * <code>clazz</code> and <code>type</code> of field.
178 *
179 * @param clazz the Class from which to retrieve the static fields
180 * @param type the type of static fields to retrieve, if null all are retrieved
181 * @return Collection the collection of static field values.
182 * @throws IllegalAccessException - if some aspect of this static field prevents it from being added to this collection.
183 */
184 public static Collection<Object> getStaticFieldValues(
185 final Class clazz,
186 final Class type)
187 throws IllegalAccessException
188 {
189 ExceptionUtils.checkNull(
190 "clazz",
191 clazz);
192 final Field[] fields = clazz.getFields();
193 int fieldsNum = fields.length;
194
195 final List<Object> values = new ArrayList<Object>();
196 Field field;
197 int modifiers;
198 for (int ctr = 0; ctr < fieldsNum; ctr++)
199 {
200 field = fields[ctr];
201 modifiers = field.getModifiers();
202 if (Modifier.isStatic(modifiers))
203 {
204 if (type != null)
205 {
206 if (type == field.getType())
207 {
208 values.add(fields[ctr].get(null));
209 }
210 }
211 else
212 {
213 values.add(fields[ctr].get(null));
214 }
215 }
216 }
217 return values;
218 }
219
220 /**
221 * Retrieves all interfaces for the given <code>className</code> (including <code>className</code>
222 * itself, assuming it's an interface as well).
223 *
224 * @param className the root interface className
225 * @return a list containing all interfaces ordered from the root down.
226 */
227 public static List<Class> getInterfaces(final String className)
228 {
229 final List<Class> interfaces = new ArrayList<Class>();
230 if (StringUtils.isNotBlank(className))
231 {
232 interfaces.addAll(getInterfaces(ClassUtils.loadClass(className.trim())));
233 }
234 return interfaces;
235 }
236
237 /**
238 * Retrieves all interfaces for the given <code>clazz</code> (including <code>clazz</code>
239 * itself, assuming it's an interface as well).
240 *
241 * @param clazz the root interface class
242 * @return a list containing all interfaces ordered from the root down.
243 */
244 public static List<Class> getInterfaces(final Class clazz)
245 {
246 final List<Class> interfaces = new ArrayList<Class>();
247 if (clazz != null)
248 {
249 interfaces.addAll(ClassUtils.getAllInterfaces(clazz));
250 if (clazz.isInterface())
251 {
252 interfaces.add(
253 0,
254 clazz);
255 }
256 }
257 return interfaces;
258 }
259
260 /**
261 * Gets the interfaces for the given <code>className</code> in reverse order.
262 *
263 * @param className the name of the class for which to retrieve the interfaces
264 * @return the array containing the reversed interfaces.
265 */
266 public static Class[] getInterfacesReversed(final String className)
267 {
268 final List<Class> interfacesList = getInterfaces(className);
269 Class[] interfaces = interfacesList.toArray(new Class[interfacesList.size()]);
270 if (interfaces.length > 0)
271 {
272 CollectionUtils.reverseArray(interfaces);
273 }
274 return interfaces;
275 }
276
277 /**
278 * <p>
279 * Checks if a given type name is a Java primitive type. </p>
280 *
281 * @param name a <code>String</code> with the name of the type
282 * @return <code>true</code> if <code>name</code> is a Java primitive type; <code>false</code> if not
283 */
284 protected static boolean isPrimitiveType(final String name)
285 {
286 return "void".equals(name) || "char".equals(name) || "byte".equals(name) || "short".equals(name) ||
287 "int".equals(name) || "long".equals(name) || "float".equals(name) || "double".equals(name) ||
288 "boolean".equals(name);
289 }
290
291 /**
292 * The suffix for class files.
293 */
294 public static final String CLASS_EXTENSION = ".class";
295
296 /**
297 * Searches the contents of the <code>directoryUri</code> and returns the first
298 * Class found that is of the given <code>type</code>.
299 *
300 * @param directoryUris the URIs to search, ie. directories or archives.
301 * @param type the type to find.
302 * @return the class or null if not found.
303 */
304 public static Class findClassOfType(
305 final URL[] directoryUris,
306 final Class type)
307 {
308 Class found = null;
309 if (directoryUris != null && directoryUris.length > 0)
310 {
311 for (URL directoryUri : directoryUris)
312 {
313 final List<String> contents = ResourceUtils.getDirectoryContents(
314 directoryUri,
315 false,
316 null);
317 for (final String path : contents)
318 {
319 if (path.endsWith(CLASS_EXTENSION))
320 {
321 final String typeName =
322 StringUtils.replace(
323 ResourceUtils.normalizePath(path).replace('/', '.'),
324 CLASS_EXTENSION,
325 "");
326 try
327 {
328 final Class loadedClass = getClassLoader().loadClass(typeName);
329 if (type.isAssignableFrom(loadedClass))
330 {
331 found = loadedClass;
332 break;
333 }
334 }
335 catch (final ClassNotFoundException exception)
336 {
337 // - ignore, means the file wasn't a class
338 }
339 }
340 }
341 }
342 }
343 return found;
344 }
345
346 /**
347 * Loads all methods from the given <code>clazz</code> (this includes
348 * all super class methods, public, private and protected).
349 *
350 * @param clazz the class to retrieve the methods.
351 * @return the loaded methods.
352 */
353 public static List<Method> getAllMethods(final Class clazz)
354 {
355 final Set<Method> methods = new LinkedHashSet<Method>();
356 loadMethods(clazz, methods);
357 return new ArrayList<Method>(methods);
358 }
359
360 /**
361 * Loads all methods from the given <code>clazz</code> (this includes
362 * all super class methods).
363 *
364 * @param methods the list to load full of methods.
365 * @param clazz the class to retrieve the methods.
366 */
367 private static void loadMethods(
368 final Class clazz,
369 final Set<Method> methods)
370 {
371 methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
372 if (clazz.getSuperclass() != null)
373 {
374 loadMethods(
375 clazz.getSuperclass(),
376 methods);
377 }
378 }
379
380 /**
381 * Indicates whether or not a class of the given <code>type</code>
382 * is present in one of the given <code>directoryUris</code>.
383 *
384 * @param directoryUris the URIs to search, ie. directories or archives.
385 * @param type the type to check.
386 * @return true/false.
387 */
388 public static boolean isClassOfTypePresent(
389 final URL[] directoryUris,
390 final Class type)
391 {
392 return ClassUtils.findClassOfType(directoryUris, type) != null;
393 }
394 }