/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.springframework.context.annotation.support;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.asm.ClassReader;
import org.springframework.asm.commons.EmptyVisitor;
/**
* Various utility methods commonly used when interacting with ASM.
*/
class AsmUtils {
public static final EmptyVisitor EMPTY_VISITOR = new EmptyVisitor();
private static final Log log = LogFactory.getLog(AsmUtils.class);
/**
* @param className a standard, dot-delimeted, fully-qualified Java class name
* @return internal version of className, as per ASM guide section 2.1.2
* "Internal Names"
*/
public static String convertClassNameToInternalName(String className) {
return className.replace('.', '/');
}
/**
* Convert a type descriptor to a classname suitable for classloading with
* Class.forName().
*
* @param typeDescriptor see ASM guide section 2.1.3
*/
public static String convertTypeDescriptorToClassName(String typeDescriptor) {
final String internalName; // See ASM guide section 2.1.2
// TODO: SJC-242 should catch all possible cases. use case statement and switch on
// char
// TODO: SJC-242 converting from primitive to object here won't be intuitive to
// users
if ("V".equals(typeDescriptor))
return Void.class.getName();
if ("I".equals(typeDescriptor))
return Integer.class.getName();
if ("Z".equals(typeDescriptor))
return Boolean.class.getName();
// strip the leading array/object/primitive identifier
if (typeDescriptor.startsWith("[["))
internalName = typeDescriptor.substring(3);
else if (typeDescriptor.startsWith("["))
internalName = typeDescriptor.substring(2);
else
internalName = typeDescriptor.substring(1);
// convert slashes to dots
String className = internalName.replace('/', '.');
// and strip trailing semicolon (if present)
if (className.endsWith(";"))
className = className.substring(0, internalName.length() - 1);
return className;
}
/**
* @param methodDescriptor see ASM guide section 2.1.4
*/
public static String getReturnTypeFromMethodDescriptor(String methodDescriptor) {
String returnTypeDescriptor = methodDescriptor.substring(methodDescriptor.indexOf(')') + 1);
return convertTypeDescriptorToClassName(returnTypeDescriptor);
}
/**
* Creates a new ASM {@link ClassReader} for pathToClass. Appends '.class' to
* pathToClass before attempting to load.
*
* @throws RuntimeException if pathToClass+.class cannot be found on the
* classpath
* @throws RuntimeException if an IOException occurs when creating the new ClassReader
*/
public static ClassReader newClassReader(String pathToClass, ClassLoader classLoader) {
InputStream is = Util.getClassAsStream(pathToClass, classLoader);
return newClassReader(is);
}
/**
* Convenience method that simply returns a new ASM {@link ClassReader} instance based
* on the supplied bytes byte array. This method is exactly equivalent to
* calling new ClassReader(byte[]), and is mainly provided for symmetry with usage of
* {@link #newClassReader(InputStream)}.
*
* @param bytes byte array that will be provided as input to the new ClassReader
* instance.
*/
public static ClassReader newClassReader(byte[] bytes) {
return new ClassReader(bytes);
}
/**
* Convenience method that creates and returns a new ASM {@link ClassReader} for the
* given InputStream is, closing the InputStream after creating the
* ClassReader and rethrowing any IOException thrown during ClassReader instantiation as
* an unchecked exception. Logs and ignores any IOException thrown when closing the
* InputStream.
*
* @param is InputStream that will be provided to the new ClassReader instance.
*/
public static ClassReader newClassReader(InputStream is) {
try {
return new ClassReader(is);
} catch (IOException ex) {
throw new RuntimeException("An unexpected exception occurred while creating ASM ClassReader: " + ex);
} finally {
try {
is.close();
} catch (IOException ex) {
log.error("Ignoring exception thrown while closing InputStream", ex);
}
}
}
}