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 }