001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 021 package org.apache.directory.shared.ldap.util; 022 023 024 import java.beans.Introspector; 025 import java.lang.reflect.Array; 026 import java.lang.reflect.Method; 027 import java.lang.reflect.Modifier; 028 import java.util.HashSet; 029 import java.util.Set; 030 031 032 /** 033 * Miscellaneous class utility methods. Mainly for internal use within the 034 * framework; consider Jakarta's Commons Lang for a more comprehensive suite 035 * of utilities. 036 * 037 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 038 */ 039 public abstract class SpringClassUtils 040 { 041 042 /** Suffix for array class names */ 043 public static final String ARRAY_SUFFIX = "[]"; 044 045 /** All primitive classes */ 046 private static Class[] PRIMITIVE_CLASSES = 047 { boolean.class, byte.class, char.class, short.class, int.class, long.class, float.class, double.class }; 048 049 /** The package separator character '.' */ 050 private static final char PACKAGE_SEPARATOR_CHAR = '.'; 051 052 /** The inner class separator character '$' */ 053 private static final char INNER_CLASS_SEPARATOR_CHAR = '$'; 054 055 /** The CGLIB class separator character "$$" */ 056 private static final String CGLIB_CLASS_SEPARATOR_CHAR = "$$"; 057 058 059 /** 060 * Return a default ClassLoader to use (never <code>null</code>). 061 * Returns the thread context ClassLoader, if available. 062 * The ClassLoader that loaded the ClassUtils class will be used as fallback. 063 * <p>Call this method if you intend to use the thread context ClassLoader 064 * in a scenario where you absolutely need a non-null ClassLoader reference: 065 * for example, for class path resource loading (but not necessarily for 066 * <code>Class.forName</code>, which accepts a <code>null</code> ClassLoader 067 * reference as well). 068 * @see java.lang.Thread#getContextClassLoader() 069 */ 070 public static ClassLoader getDefaultClassLoader() 071 { 072 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 073 if ( cl == null ) 074 { 075 // No thread context class loader -> use class loader of this class. 076 cl = SpringClassUtils.class.getClassLoader(); 077 } 078 return cl; 079 } 080 081 082 /** 083 * Replacement for <code>Class.forName()</code> that also returns Class instances 084 * for primitives (like "int") and array class names (like "String[]"). 085 * <p>Always uses the thread context class loader. 086 * @param name the name of the Class 087 * @return Class instance for the supplied name 088 * @see java.lang.Class#forName(String, boolean, ClassLoader) 089 * @see java.lang.Thread#getContextClassLoader() 090 */ 091 public static Class forName( String name ) throws ClassNotFoundException 092 { 093 return forName( name, Thread.currentThread().getContextClassLoader() ); 094 } 095 096 097 /** 098 * Replacement for <code>Class.forName()</code> that also returns Class instances 099 * for primitives (like "int") and array class names (like "String[]"). 100 * @param name the name of the Class 101 * @param classLoader the class loader to use 102 * @return Class instance for the supplied name 103 * @see java.lang.Class#forName(String, boolean, ClassLoader) 104 * @see java.lang.Thread#getContextClassLoader() 105 */ 106 public static Class forName( String name, ClassLoader classLoader ) throws ClassNotFoundException 107 { 108 Class clazz = resolvePrimitiveClassName( name ); 109 if ( clazz != null ) 110 { 111 return clazz; 112 } 113 if ( name.endsWith( ARRAY_SUFFIX ) ) 114 { 115 // special handling for array class names 116 String elementClassName = name.substring( 0, name.length() - ARRAY_SUFFIX.length() ); 117 Class elementClass = SpringClassUtils.forName( elementClassName, classLoader ); 118 return Array.newInstance( elementClass, 0 ).getClass(); 119 } 120 return Class.forName( name, true, classLoader ); 121 } 122 123 124 /** 125 * Resolve the given class name as primitive class, if appropriate. 126 * @param name the name of the potentially primitive class 127 * @return the primitive class, or <code>null</code> if the name does not denote 128 * a primitive class 129 */ 130 public static Class resolvePrimitiveClassName( String name ) 131 { 132 // Most class names will be quite long, considering that they 133 // SHOULD sit in a package, so a length check is worthwhile. 134 if ( name.length() <= 8 ) 135 { 136 // could be a primitive - likely 137 for ( int i = 0; i < PRIMITIVE_CLASSES.length; i++ ) 138 { 139 Class clazz = PRIMITIVE_CLASSES[i]; 140 if ( clazz.getName().equals( name ) ) 141 { 142 return clazz; 143 } 144 } 145 } 146 return null; 147 } 148 149 150 /** 151 * Return the short string name of a Java class in decapitalized 152 * JavaBeans property format. 153 * @param clazz the class 154 * @return the short name rendered in a standard JavaBeans property format 155 * @see java.beans.Introspector#decapitalize(String) 156 */ 157 public static String getShortNameAsProperty( Class clazz ) 158 { 159 return Introspector.decapitalize( getShortName( clazz ) ); 160 } 161 162 163 /** 164 * Get the class name without the qualified package name. 165 * @param clazz the class to get the short name for 166 * @return the class name of the class without the package name 167 * @throws IllegalArgumentException if the class is null 168 */ 169 public static String getShortName( Class clazz ) 170 { 171 return getShortName( clazz.getName() ); 172 } 173 174 175 /** 176 * Get the class name without the qualified package name. 177 * @param className the className to get the short name for 178 * @return the class name of the class without the package name 179 * @throws IllegalArgumentException if the className is empty 180 */ 181 public static String getShortName( String className ) 182 { 183 //Assert.hasLength(className, "class name must not be empty"); 184 int lastDotIndex = className.lastIndexOf( PACKAGE_SEPARATOR_CHAR ); 185 int nameEndIndex = className.indexOf( CGLIB_CLASS_SEPARATOR_CHAR ); 186 if ( nameEndIndex == -1 ) 187 { 188 nameEndIndex = className.length(); 189 } 190 String shortName = className.substring( lastDotIndex + 1, nameEndIndex ); 191 shortName = shortName.replace( INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR ); 192 return shortName; 193 } 194 195 196 /** 197 * Return the qualified name of the given method, consisting of 198 * fully qualified interface/class name + "." + method name. 199 * @param method the method 200 * @return the qualified name of the method 201 */ 202 public static String getQualifiedMethodName( Method method ) 203 { 204 //Assert.notNull(method, "Method must not be empty"); 205 return method.getDeclaringClass().getName() + "." + method.getName(); 206 } 207 208 209 /** 210 * Determine whether the given class has a method with the given signature. 211 * Essentially translates <code>NoSuchMethodException</code> to "false". 212 * @param clazz the clazz to analyze 213 * @param methodName the name of the method 214 * @param paramTypes the parameter types of the method 215 */ 216 public static boolean hasMethod( Class clazz, String methodName, Class[] paramTypes ) 217 { 218 try 219 { 220 clazz.getMethod( methodName, paramTypes ); 221 return true; 222 } 223 catch ( NoSuchMethodException ex ) 224 { 225 return false; 226 } 227 } 228 229 230 /** 231 * Return the number of methods with a given name (with any argument types), 232 * for the given class and/or its superclasses. Includes non-public methods. 233 * @param clazz the clazz to check 234 * @param methodName the name of the method 235 * @return the number of methods with the given name 236 */ 237 public static int getMethodCountForName( Class clazz, String methodName ) 238 { 239 int count = 0; 240 do 241 { 242 for ( int i = 0; i < clazz.getDeclaredMethods().length; i++ ) 243 { 244 Method method = clazz.getDeclaredMethods()[i]; 245 if ( methodName.equals( method.getName() ) ) 246 { 247 count++; 248 } 249 } 250 clazz = clazz.getSuperclass(); 251 } 252 while ( clazz != null ); 253 return count; 254 } 255 256 257 /** 258 * Does the given class and/or its superclasses at least have one or more 259 * methods (with any argument types)? Includes non-public methods. 260 * @param clazz the clazz to check 261 * @param methodName the name of the method 262 * @return whether there is at least one method with the given name 263 */ 264 public static boolean hasAtLeastOneMethodWithName( Class clazz, String methodName ) 265 { 266 do 267 { 268 for ( int i = 0; i < clazz.getDeclaredMethods().length; i++ ) 269 { 270 Method method = clazz.getDeclaredMethods()[i]; 271 272 if ( methodName.equals( method.getName() ) ) 273 { 274 return true; 275 } 276 } 277 clazz = clazz.getSuperclass(); 278 } 279 while ( clazz != null ); 280 281 return false; 282 } 283 284 285 /** 286 * Return a static method of a class. 287 * @param methodName the static method name 288 * @param clazz the class which defines the method 289 * @param args the parameter types to the method 290 * @return the static method, or <code>null</code> if no static method was found 291 * @throws IllegalArgumentException if the method name is blank or the clazz is null 292 */ 293 public static Method getStaticMethod( Class clazz, String methodName, Class[] args ) 294 { 295 try 296 { 297 Method method = clazz.getDeclaredMethod( methodName, args ); 298 299 if ( ( method.getModifiers() & Modifier.STATIC ) != 0 ) 300 { 301 return method; 302 } 303 } 304 catch ( NoSuchMethodException ex ) 305 { 306 } 307 308 return null; 309 } 310 311 312 /** 313 * Return a path suitable for use with ClassLoader.getResource (also 314 * suitable for use with Class.getResource by prepending a slash ('/') to 315 * the return value. Built by taking the package of the specified class 316 * file, converting all dots ('.') to slashes ('/'), adding a trailing slash 317 * if necesssary, and concatenating the specified resource name to this. 318 * <br/>As such, this function may be used to build a path suitable for 319 * loading a resource file that is in the same package as a class file, 320 * although {link org.springframework.core.io.ClassPathResource} is usually 321 * even more convenient. 322 * @param clazz the Class whose package will be used as the base 323 * @param resourceName the resource name to append. A leading slash is optional. 324 * @return the built-up resource path 325 * @see java.lang.ClassLoader#getResource 326 * @see java.lang.Class#getResource 327 */ 328 public static String addResourcePathToPackagePath( Class clazz, String resourceName ) 329 { 330 if ( !resourceName.startsWith( "/" ) ) 331 { 332 return classPackageAsResourcePath( clazz ) + "/" + resourceName; 333 } 334 335 return classPackageAsResourcePath( clazz ) + resourceName; 336 } 337 338 339 /** 340 * Given an input class object, return a string which consists of the 341 * class's package name as a pathname, i.e., all dots ('.') are replaced by 342 * slashes ('/'). Neither a leading nor trailing slash is added. The result 343 * could be concatenated with a slash and the name of a resource, and fed 344 * directly to ClassLoader.getResource(). For it to be fed to Class.getResource, 345 * a leading slash would also have to be prepended to the return value. 346 * @param clazz the input class. A null value or the default (empty) package 347 * will result in an empty string ("") being returned. 348 * @return a path which represents the package name 349 * @see java.lang.ClassLoader#getResource 350 * @see java.lang.Class#getResource 351 */ 352 public static String classPackageAsResourcePath( Class clazz ) 353 { 354 if ( clazz == null || clazz.getPackage() == null ) 355 { 356 return ""; 357 } 358 359 return clazz.getPackage().getName().replace( '.', '/' ); 360 } 361 362 363 /** 364 * Return all interfaces that the given object implements as array, 365 * including ones implemented by superclasses. 366 * @param object the object to analyse for interfaces 367 * @return all interfaces that the given object implements as array 368 */ 369 public static Class[] getAllInterfaces( Object object ) 370 { 371 Set<Class> interfaces = getAllInterfacesAsSet( object ); 372 return interfaces.toArray( new Class[interfaces.size()] ); 373 } 374 375 376 /** 377 * Return all interfaces that the given class implements as array, 378 * including ones implemented by superclasses. 379 * @param clazz the class to analyse for interfaces 380 * @return all interfaces that the given object implements as array 381 */ 382 public static Class[] getAllInterfacesForClass( Class clazz ) 383 { 384 Set<Class> interfaces = getAllInterfacesForClassAsSet( clazz ); 385 return interfaces.toArray( new Class[interfaces.size()] ); 386 } 387 388 389 /** 390 * Return all interfaces that the given object implements as List, 391 * including ones implemented by superclasses. 392 * @param object the object to analyse for interfaces 393 * @return all interfaces that the given object implements as List 394 */ 395 public static Set<Class> getAllInterfacesAsSet( Object object ) 396 { 397 return getAllInterfacesForClassAsSet( object.getClass() ); 398 } 399 400 401 /** 402 * Return all interfaces that the given class implements as Set, 403 * including ones implemented by superclasses. 404 * @param clazz the class to analyse for interfaces 405 * @return all interfaces that the given object implements as Set 406 */ 407 public static Set<Class> getAllInterfacesForClassAsSet( Class clazz ) 408 { 409 Set<Class> interfaces = new HashSet<Class>(); 410 411 while ( clazz != null ) 412 { 413 for ( int i = 0; i < clazz.getInterfaces().length; i++ ) 414 { 415 Class ifc = clazz.getInterfaces()[i]; 416 interfaces.add( ifc ); 417 } 418 419 clazz = clazz.getSuperclass(); 420 } 421 422 return interfaces; 423 } 424 425 }