package com.gmail.zahusek.tinyprotocolapi.asm.reflection;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.WeakHashMap;
import org.bukkit.Bukkit;
class ClassManager extends ClassLoader {
private static final HashMap<String, Class<?>> classes = new HashMap<>();
private static final String v = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
private static final WeakHashMap<ClassLoader, WeakReference<ClassManager>> accessClassLoaders = new WeakHashMap<>();
private static final ClassLoader selfContextParentClassLoader = getParentClassLoader(ClassManager.class);
private static volatile ClassManager selfContextAccessClassLoader = new ClassManager(selfContextParentClassLoader);
static ClassManager get(String type) throws ClassNotFoundException {
Class<?> clazz = classes.get(type) != null ? classes.get(type) : Class.forName(type.replace("{v}", v)) ;
if(!classes.containsKey(type))
classes.put(type, clazz);
return get(clazz);
}
static ClassManager get(Class<?> type) {
ClassLoader parent = getParentClassLoader(type);
if (selfContextParentClassLoader.equals(parent)) {
if (selfContextAccessClassLoader == null) {
synchronized (accessClassLoaders) {
if (selfContextAccessClassLoader == null)
selfContextAccessClassLoader = new ClassManager(
selfContextParentClassLoader);
}
}
return selfContextAccessClassLoader;
}
synchronized (accessClassLoaders) {
WeakReference<ClassManager> ref = accessClassLoaders.get(parent);
if (ref != null) {
ClassManager accessClassLoader = ref.get();
if (accessClassLoader != null)
return accessClassLoader;
else
accessClassLoaders.remove(parent); // the value has been
// GC-reclaimed, but
// still not the key
// (defensive sanity)
}
ClassManager accessClassLoader = new ClassManager(parent);
accessClassLoaders.put(parent, new WeakReference<ClassManager>(
accessClassLoader));
return accessClassLoader;
}
}
public static void remove(ClassLoader parent) {
if (selfContextParentClassLoader.equals(parent)) {
selfContextAccessClassLoader = null;
} else {
synchronized (accessClassLoaders) {
accessClassLoaders.remove(parent);
}
}
}
public static int activeAccessClassLoaders() {
int sz = accessClassLoaders.size();
if (selfContextAccessClassLoader != null)
sz++;
return sz;
}
private ClassManager(ClassLoader parent) {
super(parent);
}
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
if (name.equals(Access.class.getName()))
return Access.class;
return super.loadClass(name, resolve);
}
Class<?> defineClass(String name, byte[] bytes) throws ClassFormatError {
try {
Method method = ClassLoader.class.getDeclaredMethod("defineClass",
new Class[]{String.class, byte[].class, int.class,
int.class, ProtectionDomain.class});
if (!method.isAccessible())
method.setAccessible(true);
return (Class<?>) method.invoke(getParent(), new Object[]{name,
bytes, Integer.valueOf(0), Integer.valueOf(bytes.length),
getClass().getProtectionDomain()});
} catch (Exception ignored) {
}
return defineClass(name, bytes, 0, bytes.length, getClass()
.getProtectionDomain());
}
static ClassLoader getParentClassLoader(Class<?> type) {
ClassLoader parent = type.getClassLoader();
if (parent == null)
parent = ClassLoader.getSystemClassLoader();
return parent;
}
}