View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
4    */
5   package mockit.internal.util;
6   
7   import edu.umd.cs.findbugs.annotations.NonNull;
8   import edu.umd.cs.findbugs.annotations.Nullable;
9   
10  import java.util.Map;
11  import java.util.concurrent.ConcurrentHashMap;
12  
13  import mockit.internal.state.TestRun;
14  
15  public final class ClassLoad {
16      public static final String OBJECT = "java/lang/Object";
17  
18      private static final ClassLoader THIS_CL = ClassLoad.class.getClassLoader();
19      private static final Map<String, Class<?>> LOADED_CLASSES = new ConcurrentHashMap<>();
20      private static final Map<String, String> SUPER_CLASSES = new ConcurrentHashMap<>();
21  
22      private ClassLoad() {
23      }
24  
25      public static void registerLoadedClass(@NonNull Class<?> aClass) {
26          LOADED_CLASSES.put(aClass.getName(), aClass);
27      }
28  
29      @NonNull
30      public static <T> Class<T> loadByInternalName(@NonNull String internalClassName) {
31          return loadClass(internalClassName.replace('/', '.'));
32      }
33  
34      @NonNull
35      public static <T> Class<T> loadClass(@NonNull String className) {
36          @Nullable
37          Class<?> loadedClass = LOADED_CLASSES.get(className);
38  
39          if (loadedClass == null) {
40              try {
41                  loadedClass = loadClassFromAClassLoader(className);
42              } catch (LinkageError e) {
43                  e.printStackTrace();
44                  throw e;
45              }
46          }
47  
48          // noinspection unchecked
49          return (Class<T>) loadedClass;
50      }
51  
52      @NonNull
53      private static Class<?> loadClassFromAClassLoader(@NonNull String className) {
54          Class<?> loadedClass = loadClass(null, className);
55  
56          if (loadedClass == null) {
57              if (className.startsWith("mockit.")) {
58                  loadedClass = loadClass(THIS_CL, className);
59              }
60  
61              if (loadedClass == null) {
62                  Class<?> testClass = TestRun.getCurrentTestClass();
63                  loadedClass = testClass == null ? null : loadClass(testClass.getClassLoader(), className);
64  
65                  if (loadedClass == null) {
66                      ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
67                      loadedClass = loadClass(contextCL, className);
68  
69                      if (loadedClass == null) {
70                          throw new IllegalArgumentException("No class with name \"" + className + "\" found");
71                      }
72                  }
73              }
74          }
75  
76          return loadedClass;
77      }
78  
79      @NonNull
80      public static <T> Class<T> loadClassAtStartup(@NonNull String className) {
81          ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
82          Class<?> loadedClass;
83  
84          try {
85              loadedClass = loadClass(contextCL, className);
86  
87              if (loadedClass == null) {
88                  loadedClass = loadClass(THIS_CL, className);
89  
90                  if (loadedClass == null) {
91                      throw new IllegalArgumentException("No class with name \"" + className + "\" found");
92                  }
93              }
94          } catch (LinkageError e) {
95              e.printStackTrace();
96              throw e;
97          }
98  
99          // noinspection unchecked
100         return (Class<T>) loadedClass;
101     }
102 
103     @Nullable
104     public static Class<?> loadClass(@Nullable ClassLoader loader, @NonNull String className) {
105         try {
106             return Class.forName(className, false, loader);
107         } catch (ClassNotFoundException ignore) {
108             return null;
109         }
110     }
111 
112     @NonNull
113     public static <T> Class<T> loadFromLoader(@Nullable ClassLoader loader, @NonNull String className) {
114         try {
115             // noinspection unchecked
116             return (Class<T>) Class.forName(className, false, loader);
117         } catch (ClassNotFoundException ignore) {
118             throw new IllegalArgumentException("No class with name \"" + className + "\" found");
119         }
120     }
121 
122     @Nullable
123     public static <T> Class<? extends T> searchTypeInClasspath(@NonNull String typeName) {
124         return searchTypeInClasspath(typeName, false);
125     }
126 
127     @Nullable
128     public static <T> Class<? extends T> searchTypeInClasspath(@NonNull String typeName, boolean initializeType) {
129         // noinspection OverlyBroadCatchBlock
130         try {
131             // noinspection unchecked
132             return (Class<? extends T>) Class.forName(typeName, initializeType, THIS_CL);
133         } catch (Throwable ignore) {
134             return null;
135         }
136     }
137 
138     public static void addSuperClass(@NonNull String classInternalName, @NonNull String superClassInternalName) {
139         SUPER_CLASSES.put(classInternalName.intern(), superClassInternalName.intern());
140     }
141 
142     @NonNull
143     public static String getSuperClass(@NonNull String classInternalName) {
144         String classDesc = classInternalName.intern();
145         String superName = SUPER_CLASSES.get(classDesc);
146 
147         if (superName == null) {
148             Class<?> theClass = loadByInternalName(classDesc);
149             Class<?> superClass = theClass.getSuperclass();
150 
151             if (superClass != null) {
152                 superName = superClass.getName().replace('.', '/').intern();
153                 SUPER_CLASSES.put(classDesc, superName);
154             }
155         }
156 
157         return superName == null ? OBJECT : superName;
158     }
159 
160     @Nullable
161     public static String whichIsSuperClass(@NonNull String internalClassName1, @NonNull String internalClassName2) {
162         String class1 = actualSuperClass(internalClassName1, internalClassName2);
163 
164         if (class1 != null) {
165             return class1;
166         }
167 
168         return actualSuperClass(internalClassName2, internalClassName1);
169     }
170 
171     @Nullable
172     private static String actualSuperClass(@NonNull String candidateSuperClass, @NonNull String candidateSubclass) {
173         String subclass = candidateSubclass;
174 
175         while (true) {
176             String superClass = getSuperClass(subclass);
177 
178             if (superClass.equals(OBJECT)) {
179                 return null;
180             }
181 
182             if (superClass.equals(candidateSuperClass)) {
183                 return candidateSuperClass;
184             }
185 
186             subclass = superClass;
187         }
188     }
189 
190     public static boolean isClassLoaderWithNoDirectAccess(@Nullable ClassLoader classLoader) {
191         return classLoader == null || classLoader != THIS_CL && classLoader.getParent() != THIS_CL;
192     }
193 
194     public static ClassLoader getClassLoaderWithAccess(@NonNull Class<?> classToBeAccessed) {
195         ClassLoader cl = classToBeAccessed.getClassLoader();
196         return isClassLoaderWithNoDirectAccess(cl) ? THIS_CL : cl;
197     }
198 }