package kilim.mirrors;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import kilim.KilimClassLoader;
import org.objectweb.asm.Type;
/**
* This class provides the Mirrors facade over a set of Class objects
* @see Mirrors
*/
public class RuntimeClassMirrors implements Mirrors {
// Weakly cache the mirror objects.
Map<String, RuntimeClassMirror> cachedClasses = Collections
.synchronizedMap(new WeakHashMap<String, RuntimeClassMirror>());
public final KilimClassLoader classLoader;
public RuntimeClassMirrors() {
this(Thread.currentThread().getContextClassLoader());
}
public RuntimeClassMirrors(ClassLoader cl) {
if (!(cl instanceof KilimClassLoader)) {
cl = new KilimClassLoader(cl);
}
this.classLoader = (KilimClassLoader) cl;
}
@Override
public ClassMirror classForName(String className) throws ClassMirrorNotFoundException {
try {
RuntimeClassMirror ret = cachedClasses.get(className);
if (ret == null) {
ret = make(classLoader.loadClass(className));
}
return ret;
} catch (ClassNotFoundException e) {
throw new ClassMirrorNotFoundException(className, e);
}
}
@Override
public ClassMirror mirror(Class<?> clazz) {
if (clazz == null)
return null;
return make(clazz);
}
@Override
public ClassMirror mirror(String className, byte[] bytecode) {
try {
return classForName(className);
} catch (ClassMirrorNotFoundException ignore) {}
return null;
}
/**
* Like classForName, but only if the class is already loaded. This does not force loading of a
* class.
*
* @param className
* @return null if className not loaded, else a RuntimeClassMirror to represent the loaded
* class.
*/
public ClassMirror loadedClassForName(String className) {
Class<?> c = classLoader.getLoadedClass(className);
return (c == null) ? null : make(c);
}
public Class<?> getLoadedClass(String className) {
return classLoader.getLoadedClass(className);
}
public boolean isLoaded(String className) {
return classLoader.isLoaded(className);
}
private RuntimeClassMirror make(Class<?> c) {
if (c == null) {
throw new NullPointerException();
}
RuntimeClassMirror ret = new RuntimeClassMirror(c);
cachedClasses.put(c.getName(), ret);
return ret;
}
}
class RuntimeMethodMirror implements MethodMirror {
private final Method method;
public RuntimeMethodMirror(Method method) {
this.method = method;
}
public String getName() {
return method.getName();
}
public String[] getExceptionTypes() {
String[] ret = new String[method.getExceptionTypes().length];
int i = 0;
for (Class<?> excl : method.getExceptionTypes()) {
ret[i++] = excl.getName();
}
return ret;
}
public String getMethodDescriptor() {
return Type.getMethodDescriptor(method);
}
public boolean isBridge() {
return method.isBridge();
}
}
class RuntimeClassMirror implements ClassMirror {
private final Class<?> clazz;
private MethodMirror[] methods;
public RuntimeClassMirror(Class<?> clazz) {
this.clazz = clazz;
}
@Override
public String getName() {
return clazz.getName();
}
@Override
public boolean isInterface() {
return clazz.isInterface();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ClassMirror) {
return ((ClassMirror) obj).getName().equals(this.getName());
}
return false;
}
@Override
public int hashCode() {
return clazz.hashCode();
}
@Override
public MethodMirror[] getDeclaredMethods() {
if (methods == null) {
Method[] declaredMethods = clazz.getDeclaredMethods();
methods = new MethodMirror[declaredMethods.length];
for (int i = 0; i < declaredMethods.length; i++) {
methods[i] = new RuntimeMethodMirror(declaredMethods[i]);
}
}
return methods;
}
@Override
public String[] getInterfaces() {
Class<?>[] ifs = clazz.getInterfaces();
String[] result = new String[ifs.length];
for (int i = 0; i < result.length; i++) {
result[i] = ifs[i].getName();
}
return result;
}
@Override
public String getSuperclass() {
Class<?> supcl = clazz.getSuperclass();
return supcl != null ? supcl.getName() : null;
}
@Override
public boolean isAssignableFrom(ClassMirror c) {
if (c instanceof RuntimeClassMirror) {
RuntimeClassMirror cc = (RuntimeClassMirror) c;
return clazz.isAssignableFrom(cc.clazz);
} else {
return false;
}
}
}