/*
* Copyright (c) 2006 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.util;
import java.util.*;
import java.util.concurrent.*;
import javax.annotation.*;
import mockit.internal.state.*;
public final class ClassLoad
{
public static final String OBJECT = "java/lang/Object";
private static final ClassLoader THIS_CL = ClassLoad.class.getClassLoader();
private static final Map<String, Class<?>> LOADED_CLASSES = new ConcurrentHashMap<String, Class<?>>();
private static final Map<String, String> SUPER_CLASSES = new ConcurrentHashMap<String, String>();
private ClassLoad() {}
public static void registerLoadedClass(@Nonnull Class<?> aClass)
{
LOADED_CLASSES.put(aClass.getName(), aClass);
}
@Nonnull
public static <T> Class<T> loadByInternalName(@Nonnull String internalClassName)
{
return loadClass(internalClassName.replace('/', '.'));
}
@Nonnull
public static <T> Class<T> loadClass(@Nonnull String className)
{
@Nullable Class<?> loadedClass = LOADED_CLASSES.get(className);
if (loadedClass == null) {
try {
loadedClass = loadClassFromAClassLoader(className);
}
catch (LinkageError e) {
e.printStackTrace();
throw e;
}
}
//noinspection unchecked
return (Class<T>) loadedClass;
}
@Nonnull
private static Class<?> loadClassFromAClassLoader(@Nonnull String className)
{
Class<?> loadedClass = loadClass(null, className);
if (loadedClass == null) {
if (className.startsWith("mockit.")) {
loadedClass = loadClass(THIS_CL, className);
}
if (loadedClass == null) {
Class<?> testClass = TestRun.getCurrentTestClass();
loadedClass = testClass == null ? null : loadClass(testClass.getClassLoader(), className);
if (loadedClass == null) {
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
loadedClass = loadClass(contextCL, className);
if (loadedClass == null) {
throw new IllegalArgumentException("No class with name \"" + className + "\" found");
}
}
}
}
return loadedClass;
}
@Nonnull
public static <T> Class<T> loadClassAtStartup(@Nonnull String className)
{
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
Class<?> loadedClass;
try {
loadedClass = loadClass(contextCL, className);
if (loadedClass == null) {
loadedClass = loadClass(THIS_CL, className);
if (loadedClass == null) {
throw new IllegalArgumentException("No class with name \"" + className + "\" found");
}
}
}
catch (LinkageError e) {
e.printStackTrace();
throw e;
}
//noinspection unchecked
return (Class<T>) loadedClass;
}
@Nullable
public static Class<?> loadClass(@Nullable ClassLoader loader, @Nonnull String className)
{
try { return Class.forName(className, false, loader); } catch (ClassNotFoundException ignore) { return null; }
}
@Nonnull
public static <T> Class<T> loadFromLoader(@Nullable ClassLoader loader, @Nonnull String className)
{
try {
//noinspection unchecked
return (Class<T>) Class.forName(className, false, loader);
}
catch (ClassNotFoundException ignore) {
throw new IllegalArgumentException("No class with name \"" + className + "\" found");
}
}
@Nullable
public static <T> Class<? extends T> searchTypeInClasspath(@Nonnull String typeName)
{
return searchTypeInClasspath(typeName, false);
}
@Nullable
public static <T> Class<? extends T> searchTypeInClasspath(@Nonnull String typeName, boolean initializeType)
{
//noinspection OverlyBroadCatchBlock
try {
//noinspection unchecked
return (Class<? extends T>) Class.forName(typeName, initializeType, THIS_CL);
}
catch (Throwable ignore) { return null; }
}
public static void addSuperClass(@Nonnull String classInternalName, @Nonnull String superClassInternalName)
{
SUPER_CLASSES.put(classInternalName.intern(), superClassInternalName.intern());
}
@Nonnull
public static String getSuperClass(@Nonnull String classInternalName)
{
String classDesc = classInternalName.intern();
String superName = SUPER_CLASSES.get(classDesc);
if (superName == null) {
Class<?> theClass = loadByInternalName(classDesc);
Class<?> superClass = theClass.getSuperclass();
if (superClass != null) {
superName = superClass.getName().replace('.', '/').intern();
SUPER_CLASSES.put(classDesc, superName);
}
}
return superName == null ? OBJECT : superName;
}
@Nullable
public static String whichIsSuperClass(@Nonnull String internalClassName1, @Nonnull String internalClassName2)
{
String class1 = actualSuperClass(internalClassName1, internalClassName2);
if (class1 != null) {
return class1;
}
String class2 = actualSuperClass(internalClassName2, internalClassName1);
return class2;
}
@Nullable
private static String actualSuperClass(@Nonnull String candidateSuperClass, @Nonnull String candidateSubclass)
{
String subclass = candidateSubclass;
while (true) {
String superClass = getSuperClass(subclass);
if (superClass.equals(OBJECT)) {
return null;
}
if (superClass.equals(candidateSuperClass)) {
return candidateSuperClass;
}
subclass = superClass;
}
}
public static boolean isGeneratedSubclass(@Nonnull String className)
{
int p = className.indexOf('$') + 1;
//noinspection SimplifiableIfStatement
if (p < 2 || p == className.length() || className.charAt(p) != '$') {
return false;
}
return className.contains("_$$_javassist_") || className.contains("_$$_jvst") || className.contains("CGLIB$$");
}
public static boolean isClassLoaderWithNoDirectAccess(@Nullable ClassLoader classLoader)
{
return classLoader == null || classLoader != THIS_CL && classLoader.getParent() != THIS_CL;
}
public static ClassLoader getClassLoaderWithAccess(@Nonnull Class<?> classToBeAccessed)
{
ClassLoader cl = classToBeAccessed.getClassLoader();
return isClassLoaderWithNoDirectAccess(cl) ? THIS_CL : cl;
}
}