blob: 64fd8df14b051dc4af9bf24ccb8e7281dbdc8715 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.shared.ldap.util;
import java.beans.Introspector;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;
/**
* Miscellaneous class utility methods. Mainly for internal use within the
* framework; consider Jakarta's Commons Lang for a more comprehensive suite
* of utilities.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public abstract class SpringClassUtils
{
/** Suffix for array class names */
public static final String ARRAY_SUFFIX = "[]";
/** All primitive classes */
private static Class[] PRIMITIVE_CLASSES =
{ boolean.class, byte.class, char.class, short.class, int.class, long.class, float.class, double.class };
/** The package separator character '.' */
private static final char PACKAGE_SEPARATOR_CHAR = '.';
/** The inner class separator character '$' */
private static final char INNER_CLASS_SEPARATOR_CHAR = '$';
/** The CGLIB class separator character "$$" */
private static final String CGLIB_CLASS_SEPARATOR_CHAR = "$$";
/**
* Return a default ClassLoader to use (never <code>null</code>).
* Returns the thread context ClassLoader, if available.
* The ClassLoader that loaded the ClassUtils class will be used as fallback.
* <p>Call this method if you intend to use the thread context ClassLoader
* in a scenario where you absolutely need a non-null ClassLoader reference:
* for example, for class path resource loading (but not necessarily for
* <code>Class.forName</code>, which accepts a <code>null</code> ClassLoader
* reference as well).
* @see java.lang.Thread#getContextClassLoader()
*/
public static ClassLoader getDefaultClassLoader()
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if ( cl == null )
{
// No thread context class loader -> use class loader of this class.
cl = SpringClassUtils.class.getClassLoader();
}
return cl;
}
/**
* Replacement for <code>Class.forName()</code> that also returns Class instances
* for primitives (like "int") and array class names (like "String[]").
* <p>Always uses the thread context class loader.
* @param name the name of the Class
* @return Class instance for the supplied name
* @see java.lang.Class#forName(String, boolean, ClassLoader)
* @see java.lang.Thread#getContextClassLoader()
*/
public static Class forName( String name ) throws ClassNotFoundException
{
return forName( name, Thread.currentThread().getContextClassLoader() );
}
/**
* Replacement for <code>Class.forName()</code> that also returns Class instances
* for primitives (like "int") and array class names (like "String[]").
* @param name the name of the Class
* @param classLoader the class loader to use
* @return Class instance for the supplied name
* @see java.lang.Class#forName(String, boolean, ClassLoader)
* @see java.lang.Thread#getContextClassLoader()
*/
public static Class forName( String name, ClassLoader classLoader ) throws ClassNotFoundException
{
Class clazz = resolvePrimitiveClassName( name );
if ( clazz != null )
{
return clazz;
}
if ( name.endsWith( ARRAY_SUFFIX ) )
{
// special handling for array class names
String elementClassName = name.substring( 0, name.length() - ARRAY_SUFFIX.length() );
Class elementClass = SpringClassUtils.forName( elementClassName, classLoader );
return Array.newInstance( elementClass, 0 ).getClass();
}
return Class.forName( name, true, classLoader );
}
/**
* Resolve the given class name as primitive class, if appropriate.
* @param name the name of the potentially primitive class
* @return the primitive class, or <code>null</code> if the name does not denote
* a primitive class
*/
public static Class resolvePrimitiveClassName( String name )
{
// Most class names will be quite long, considering that they
// SHOULD sit in a package, so a length check is worthwhile.
if ( name.length() <= 8 )
{
// could be a primitive - likely
for ( int i = 0; i < PRIMITIVE_CLASSES.length; i++ )
{
Class clazz = PRIMITIVE_CLASSES[i];
if ( clazz.getName().equals( name ) )
{
return clazz;
}
}
}
return null;
}
/**
* Return the short string name of a Java class in decapitalized
* JavaBeans property format.
* @param clazz the class
* @return the short name rendered in a standard JavaBeans property format
* @see java.beans.Introspector#decapitalize(String)
*/
public static String getShortNameAsProperty( Class clazz )
{
return Introspector.decapitalize( getShortName( clazz ) );
}
/**
* Get the class name without the qualified package name.
* @param clazz the class to get the short name for
* @return the class name of the class without the package name
* @throws IllegalArgumentException if the class is null
*/
public static String getShortName( Class clazz )
{
return getShortName( clazz.getName() );
}
/**
* Get the class name without the qualified package name.
* @param className the className to get the short name for
* @return the class name of the class without the package name
* @throws IllegalArgumentException if the className is empty
*/
public static String getShortName( String className )
{
//Assert.hasLength(className, "class name must not be empty");
int lastDotIndex = className.lastIndexOf( PACKAGE_SEPARATOR_CHAR );
int nameEndIndex = className.indexOf( CGLIB_CLASS_SEPARATOR_CHAR );
if ( nameEndIndex == -1 )
{
nameEndIndex = className.length();
}
String shortName = className.substring( lastDotIndex + 1, nameEndIndex );
shortName = shortName.replace( INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR );
return shortName;
}
/**
* Return the qualified name of the given method, consisting of
* fully qualified interface/class name + "." + method name.
* @param method the method
* @return the qualified name of the method
*/
public static String getQualifiedMethodName( Method method )
{
//Assert.notNull(method, "Method must not be empty");
return method.getDeclaringClass().getName() + "." + method.getName();
}
/**
* Determine whether the given class has a method with the given signature.
* Essentially translates <code>NoSuchMethodException</code> to "false".
* @param clazz the clazz to analyze
* @param methodName the name of the method
* @param paramTypes the parameter types of the method
*/
public static boolean hasMethod( Class clazz, String methodName, Class[] paramTypes )
{
try
{
clazz.getMethod( methodName, paramTypes );
return true;
}
catch ( NoSuchMethodException ex )
{
return false;
}
}
/**
* Return the number of methods with a given name (with any argument types),
* for the given class and/or its superclasses. Includes non-public methods.
* @param clazz the clazz to check
* @param methodName the name of the method
* @return the number of methods with the given name
*/
public static int getMethodCountForName( Class clazz, String methodName )
{
int count = 0;
do
{
for ( int i = 0; i < clazz.getDeclaredMethods().length; i++ )
{
Method method = clazz.getDeclaredMethods()[i];
if ( methodName.equals( method.getName() ) )
{
count++;
}
}
clazz = clazz.getSuperclass();
}
while ( clazz != null );
return count;
}
/**
* Does the given class and/or its superclasses at least have one or more
* methods (with any argument types)? Includes non-public methods.
* @param clazz the clazz to check
* @param methodName the name of the method
* @return whether there is at least one method with the given name
*/
public static boolean hasAtLeastOneMethodWithName( Class clazz, String methodName )
{
do
{
for ( int i = 0; i < clazz.getDeclaredMethods().length; i++ )
{
Method method = clazz.getDeclaredMethods()[i];
if ( methodName.equals( method.getName() ) )
{
return true;
}
}
clazz = clazz.getSuperclass();
}
while ( clazz != null );
return false;
}
/**
* Return a static method of a class.
* @param methodName the static method name
* @param clazz the class which defines the method
* @param args the parameter types to the method
* @return the static method, or <code>null</code> if no static method was found
* @throws IllegalArgumentException if the method name is blank or the clazz is null
*/
// This will suppress PMD.EmptyCatchBlock warnings in this method
@SuppressWarnings("PMD.EmptyCatchBlock")
public static Method getStaticMethod( Class clazz, String methodName, Class[] args )
{
try
{
Method method = clazz.getDeclaredMethod( methodName, args );
if ( ( method.getModifiers() & Modifier.STATIC ) != 0 )
{
return method;
}
}
catch ( NoSuchMethodException ex )
{
}
return null;
}
/**
* Return a path suitable for use with ClassLoader.getResource (also
* suitable for use with Class.getResource by prepending a slash ('/') to
* the return value. Built by taking the package of the specified class
* file, converting all dots ('.') to slashes ('/'), adding a trailing slash
* if necesssary, and concatenating the specified resource name to this.
* <br/>As such, this function may be used to build a path suitable for
* loading a resource file that is in the same package as a class file,
* although {link org.springframework.core.io.ClassPathResource} is usually
* even more convenient.
* @param clazz the Class whose package will be used as the base
* @param resourceName the resource name to append. A leading slash is optional.
* @return the built-up resource path
* @see java.lang.ClassLoader#getResource
* @see java.lang.Class#getResource
*/
public static String addResourcePathToPackagePath( Class clazz, String resourceName )
{
if ( !resourceName.startsWith( "/" ) )
{
return classPackageAsResourcePath( clazz ) + "/" + resourceName;
}
return classPackageAsResourcePath( clazz ) + resourceName;
}
/**
* Given an input class object, return a string which consists of the
* class's package name as a pathname, i.e., all dots ('.') are replaced by
* slashes ('/'). Neither a leading nor trailing slash is added. The result
* could be concatenated with a slash and the name of a resource, and fed
* directly to ClassLoader.getResource(). For it to be fed to Class.getResource,
* a leading slash would also have to be prepended to the return value.
* @param clazz the input class. A null value or the default (empty) package
* will result in an empty string ("") being returned.
* @return a path which represents the package name
* @see java.lang.ClassLoader#getResource
* @see java.lang.Class#getResource
*/
public static String classPackageAsResourcePath( Class clazz )
{
if ( clazz == null || clazz.getPackage() == null )
{
return "";
}
return clazz.getPackage().getName().replace( '.', '/' );
}
/**
* Return all interfaces that the given object implements as array,
* including ones implemented by superclasses.
* @param object the object to analyse for interfaces
* @return all interfaces that the given object implements as array
*/
public static Class[] getAllInterfaces( Object object )
{
Set<Class> interfaces = getAllInterfacesAsSet( object );
return interfaces.toArray( new Class[interfaces.size()] );
}
/**
* Return all interfaces that the given class implements as array,
* including ones implemented by superclasses.
* @param clazz the class to analyse for interfaces
* @return all interfaces that the given object implements as array
*/
public static Class[] getAllInterfacesForClass( Class clazz )
{
Set<Class> interfaces = getAllInterfacesForClassAsSet( clazz );
return interfaces.toArray( new Class[interfaces.size()] );
}
/**
* Return all interfaces that the given object implements as List,
* including ones implemented by superclasses.
* @param object the object to analyse for interfaces
* @return all interfaces that the given object implements as List
*/
public static Set<Class> getAllInterfacesAsSet( Object object )
{
return getAllInterfacesForClassAsSet( object.getClass() );
}
/**
* Return all interfaces that the given class implements as Set,
* including ones implemented by superclasses.
* @param clazz the class to analyse for interfaces
* @return all interfaces that the given object implements as Set
*/
public static Set<Class> getAllInterfacesForClassAsSet( Class clazz )
{
Set<Class> interfaces = new HashSet<Class>();
while ( clazz != null )
{
for ( int i = 0; i < clazz.getInterfaces().length; i++ )
{
Class ifc = clazz.getInterfaces()[i];
interfaces.add( ifc );
}
clazz = clazz.getSuperclass();
}
return interfaces;
}
}