package org.googlecode.perftrace.javaagent.util;
import static java.lang.String.format;
import static org.googlecode.perftrace.javaagent.util.CollectionFactory.newMap;
import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
/**
* From Tapestry 5
* Handy method useful when creating new classes using
*/
@SuppressWarnings("all")
public final class ClassFabUtils
{
private static final AtomicLong UID_GENERATOR = new AtomicLong(System.currentTimeMillis());
public static String nextUID()
{
return Long.toHexString(UID_GENERATOR.getAndIncrement());
}
/**
* Generates a unique class name, which will be in the default package.
*/
public static synchronized String generateClassName(String baseName)
{
return "$" + baseName + "_" + nextUID();
}
/**
* Returns a class name derived from the provided interfaceClass. The package part of the interface name is stripped
* out, and the result passed to {@link #generateClassName(String)}.
*/
public static String generateClassName(Class interfaceClass)
{
return generateClassName(interfaceClass.getSimpleName());
}
/**
* Javassist needs the class name to be as it appears in source code, even for arrays. Invoking getName() on a Class
* instance representing an array returns the internal format (i.e, "[...;" or something). This returns it as it
* would appear in Java code.
*/
public static String toJavaClassName(Class inputClass)
{
if (inputClass.isArray())
return toJavaClassName(inputClass.getComponentType()) + "[]";
return inputClass.getName();
}
/**
* Returns true if the method is the standard toString() method. Very few interfaces will ever include this method
* as part of the interface, but we have to be sure.
*/
public static boolean isToString(Method method)
{
if (!method.getName().equals("toString"))
return false;
if (method.getParameterTypes().length > 0)
return false;
return method.getReturnType().equals(String.class);
}
public static Class getPrimitiveType(String primitiveTypeName)
{
return PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.get(primitiveTypeName).primitiveType;
}
private static class PrimitiveInfo
{
private final Class primitiveType;
private final String typeCode;
private final Class wrapperType;
private final String unwrapMethod;
public PrimitiveInfo(Class primitiveType, String typeCode, Class wrapperType, String unwrapMethod)
{
this.primitiveType = primitiveType;
this.typeCode = typeCode;
this.wrapperType = wrapperType;
this.unwrapMethod = unwrapMethod;
}
}
private static final Map<String, PrimitiveInfo> PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO = newMap();
private static final Map<Class, PrimitiveInfo> WRAPPER_TYPE_TO_PRIMITIVE_INFO = newMap();
static
{
add(boolean.class, "Z", Boolean.class, "booleanValue");
add(short.class, "S", Short.class, "shortValue");
add(int.class, "I", Integer.class, "intValue");
add(long.class, "J", Long.class, "longValue");
add(float.class, "F", Float.class, "floatValue");
add(double.class, "D", Double.class, "doubleValue");
add(char.class, "C", Character.class, "charValue");
add(byte.class, "B", Byte.class, "byteValue");
}
private static void add(Class primitiveType, String typeCode, Class wrapperType, String unwrapMethod)
{
PrimitiveInfo info = new PrimitiveInfo(primitiveType, typeCode, wrapperType, unwrapMethod);
PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.put(primitiveType.getName(), info);
WRAPPER_TYPE_TO_PRIMITIVE_INFO.put(wrapperType, info);
}
/**
* Translates types from standard Java format to Java VM format. For example, java.util.Locale remains
* java.util.Locale, but int[][] is translated to [[I and java.lang.Object[] to [Ljava.lang.Object;
*/
public static String toJVMBinaryName(String type)
{
// if it is not an array, just return the type itself
if (!type.endsWith("[]"))
return type;
// if it is an array, convert it to JavaVM-style format
StringBuilder buffer = new StringBuilder();
while (type.endsWith("[]"))
{
buffer.append("[");
type = type.substring(0, type.length() - 2);
}
PrimitiveInfo pi = PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.get(type);
if (pi != null)
{
buffer.append(pi.typeCode);
}
else
{
buffer.append("L");
buffer.append(type);
buffer.append(";");
}
return buffer.toString();
}
/**
* Given a wrapper type, determines the corresponding primitive type.
*/
public static Class getPrimitiveType(Class wrapperType)
{
return WRAPPER_TYPE_TO_PRIMITIVE_INFO.get(wrapperType).primitiveType;
}
/**
* Returns the wrapper type for an input type; for most types, this is the type. For primitive types, it is the
* corresponding wrapper type.
*
* @param type
* type to check
* @return type or corresponding wrapper type
*/
public static Class getWrapperType(Class type)
{
PrimitiveInfo info = PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.get(type.getName());
return info == null ? type : info.wrapperType;
}
/**
* Takes a reference and casts it to the desired type. If the desired type is a primitive type, then the reference
* is cast to the correct wrapper type and a call to the correct unwrapper method is added. The end result is code
* that can be assigned to a field or parameter of the desired type (even if desired type is a primitive).
*
* @param reference
* to be cast
* @param desiredType
* desired object or primitive type
* @return Javassist code to peform the cast
*/
public static String castReference(String reference, String desiredType)
{
if (isPrimitiveType(desiredType))
{
PrimitiveInfo info = PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.get(desiredType);
return String.format("((%s)%s).%s()", info.wrapperType.getName(), reference, info.unwrapMethod);
}
return String.format("(%s)%s", desiredType, reference);
}
/**
* Given a primitive type, finds the unwrap method of the corresponding wrapper type.
*
* @param primitiveType
* @return method name
*/
public static String getUnwrapMethodName(Class primitiveType)
{
return PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.get(primitiveType.getName()).unwrapMethod;
}
/**
* Given a type name, determines if that is the name of a primitive type.
*/
public static boolean isPrimitiveType(String typeName)
{
return PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.containsKey(typeName);
}
/**
* Converts a Class to a JVM type code (the way class information is expressed in a class file).
*/
public static String getTypeCode(Class type)
{
if (type.equals(void.class))
return "V";
if (type.isPrimitive())
return PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.get(type.getName()).typeCode;
if (type.isArray())
return "[" + getTypeCode(type.getComponentType());
return "L" + type.getName().replace('.', '/') + ";";
}
/**
* Given a Class instance, convert the name into a path that can be used to locate
* the underlying class file on the classpath.
*
* @since 5.2.0
*/
public static String getPathForClass(Class clazz)
{
assert clazz != null;
return getPathForClassNamed(clazz.getName());
}
/**
* Given a fully qualified class name, converts to a path on the classpath.
*
* @since 5.2.0
*/
public static String getPathForClassNamed(String className)
{
return className.replace('.', '/') + ".class";
}
/**
* Converts a URL with protocol "file" to a File instance.
*
* @since 5.2.0
*/
public static File toFileFromFileProtocolURL(URL url)
{
assert url != null;
if (!url.getProtocol().equals("file"))
throw new IllegalArgumentException(String.format("URL %s does not use the 'file' protocol.", url));
// http://weblogs.java.net/blog/kohsuke/archive/2007/04/how_to_convert.html
try
{
return new File(url.toURI());
}
catch (URISyntaxException ex)
{
return new File(url.getPath());
}
}
}