001package org.andromda.core.common; 002 003import java.lang.reflect.Field; 004import java.lang.reflect.Method; 005import java.lang.reflect.Modifier; 006import java.net.URL; 007import java.util.ArrayList; 008import java.util.Arrays; 009import java.util.Collection; 010import java.util.LinkedHashSet; 011import java.util.List; 012import java.util.Set; 013import org.apache.commons.collections.CollectionUtils; 014import org.apache.commons.lang.StringUtils; 015 016// TODO: FindBugs: This class has a simple name that is identical to that of its superclass. This can be exceptionally confusing. 017/** 018 * Contains utilities for dealing with classes. 019 * 020 * @author Chad Brandon 021 * @author Bob Fields 022 */ 023public class ClassUtils 024 extends org.apache.commons.lang.ClassUtils 025{ 026 /** 027 * Creates a new instance of the class having the given <code>className</code>. 028 * 029 * @param className the name of the class to instantiate. 030 * @return Object the new instance 031 */ 032 public static Object newInstance(final String className) 033 { 034 try 035 { 036 return loadClass(className).newInstance(); 037 } 038 catch (final Throwable throwable) 039 { 040 throw new ClassUtilsException(throwable); 041 } 042 } 043 044 /** 045 * Creates a new instance of the class given the <code>type</code>. 046 * 047 * @param type the type from which to instantiate the new instance. 048 * @return Object the new instance 049 */ 050 public static Object newInstance(final Class type) 051 { 052 try 053 { 054 return type != null ? type.newInstance() : null; 055 } 056 catch (final Throwable throwable) 057 { 058 throw new ClassUtilsException(throwable); 059 } 060 } 061 062 /** 063 * Loads and returns the class having the className. Will load but normal classes and the classes representing 064 * primitives. 065 * 066 * @param className the name of the class to load. 067 * @return Class the loaded class 068 */ 069 public static Class loadClass(String className) 070 { 071 ExceptionUtils.checkEmpty( 072 "className", 073 className); 074 className = StringUtils.trimToNull(className); 075 076 // get rid of any array notation 077 className = StringUtils.replace( 078 className, 079 "[]", 080 ""); 081 082 final ClassLoader loader = getClassLoader(); 083 Class loadedClass; 084 try 085 { 086 // check and see if it's a primitive and if so convert it 087 if (ClassUtils.isPrimitiveType(className)) 088 { 089 loadedClass = getPrimitiveClass( 090 className, 091 loader); 092 } 093 else 094 { 095 loadedClass = loader.loadClass(className); 096 } 097 } 098 catch (final Throwable throwable) 099 { 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}