forked from serenity-bdd/serenity-core
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
166 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
168 changes: 125 additions & 43 deletions
168
serenity-junit5/src/main/java/net/serenitybdd/junit5/utils/ClassUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,89 +1,171 @@ | ||
package net.serenitybdd.junit5.utils; | ||
|
||
import java.io.Closeable; | ||
import java.io.Externalizable; | ||
import java.io.Serializable; | ||
import java.lang.reflect.Array; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.*; | ||
|
||
public class ClassUtil { | ||
|
||
private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new IdentityHashMap(9); | ||
private static final Map<String, Class<?>> primitiveTypeNameMap = new HashMap(32); | ||
private static final Map<Class<?>, Class<?>> primitiveTypeToWrapperMap = new IdentityHashMap<>(9); | ||
private static final Map<String, Class<?>> commonClassCache = new HashMap(64); | ||
|
||
/** Suffix for array class names: {@code "[]"}. */ | ||
public static final String ARRAY_SUFFIX = "[]"; | ||
|
||
/** Prefix for internal array class names: {@code "["}. */ | ||
private static final String INTERNAL_ARRAY_PREFIX = "["; | ||
|
||
/** Prefix for internal non-primitive array class names: {@code "[L"}. */ | ||
private static final String NON_PRIMITIVE_ARRAY_PREFIX = "[L"; | ||
|
||
/** The package separator character: {@code '.'}. */ | ||
private static final char PACKAGE_SEPARATOR = '.'; | ||
|
||
/** The path separator character: {@code '/'}. */ | ||
private static final char PATH_SEPARATOR = '/'; | ||
|
||
/** The nested class separator character: {@code '$'}. */ | ||
private static final char NESTED_CLASS_SEPARATOR = '$'; | ||
|
||
/** The CGLIB class separator: {@code "$$"}. */ | ||
public static final String CGLIB_CLASS_SEPARATOR = "$$"; | ||
|
||
/** The ".class" file suffix. */ | ||
public static final String CLASS_FILE_SUFFIX = ".class"; | ||
|
||
public static Class<?> forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError { | ||
|
||
Class<?> clazz = resolvePrimitiveClassName(name); | ||
if (clazz == null) { | ||
clazz = (Class)commonClassCache.get(name); | ||
clazz = commonClassCache.get(name); | ||
} | ||
|
||
if (clazz != null) { | ||
return clazz; | ||
} else { | ||
Class elementClass; | ||
String elementName; | ||
if (name.endsWith("[]")) { | ||
elementName = name.substring(0, name.length() - "[]".length()); | ||
elementClass = forName(elementName, classLoader); | ||
return Array.newInstance(elementClass, 0).getClass(); | ||
} else if (name.startsWith("[L") && name.endsWith(";")) { | ||
elementName = name.substring("[L".length(), name.length() - 1); | ||
elementClass = forName(elementName, classLoader); | ||
return Array.newInstance(elementClass, 0).getClass(); | ||
} else if (name.startsWith("[")) { | ||
elementName = name.substring("[".length()); | ||
elementClass = forName(elementName, classLoader); | ||
return Array.newInstance(elementClass, 0).getClass(); | ||
} else { | ||
ClassLoader clToUse = classLoader; | ||
if (classLoader == null) { | ||
clToUse = getDefaultClassLoader(); | ||
} | ||
} | ||
|
||
// "java.lang.String[]" style arrays | ||
if (name.endsWith(ARRAY_SUFFIX)) { | ||
String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length()); | ||
Class<?> elementClass = forName(elementClassName, classLoader); | ||
return Array.newInstance(elementClass, 0).getClass(); | ||
} | ||
|
||
// "[Ljava.lang.String;" style arrays | ||
if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) { | ||
String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1); | ||
Class<?> elementClass = forName(elementName, classLoader); | ||
return Array.newInstance(elementClass, 0).getClass(); | ||
} | ||
|
||
// "[[I" or "[[Ljava.lang.String;" style arrays | ||
if (name.startsWith(INTERNAL_ARRAY_PREFIX)) { | ||
String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length()); | ||
Class<?> elementClass = forName(elementName, classLoader); | ||
return Array.newInstance(elementClass, 0).getClass(); | ||
} | ||
|
||
ClassLoader clToUse = classLoader; | ||
if (clToUse == null) { | ||
clToUse = getDefaultClassLoader(); | ||
} | ||
try { | ||
return Class.forName(name, false, clToUse); | ||
} | ||
catch (ClassNotFoundException ex) { | ||
int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR); | ||
if (lastDotIndex != -1) { | ||
String nestedClassName = | ||
name.substring(0, lastDotIndex) + NESTED_CLASS_SEPARATOR + name.substring(lastDotIndex + 1); | ||
try { | ||
return Class.forName(name, false, clToUse); | ||
} catch (ClassNotFoundException var9) { | ||
int lastDotIndex = name.lastIndexOf(46); | ||
if (lastDotIndex != -1) { | ||
String nestedClassName = name.substring(0, lastDotIndex) + '$' + name.substring(lastDotIndex + 1); | ||
|
||
try { | ||
return Class.forName(nestedClassName, false, clToUse); | ||
} catch (ClassNotFoundException var8) { | ||
} | ||
} | ||
|
||
throw var9; | ||
return Class.forName(nestedClassName, false, clToUse); | ||
} | ||
catch (ClassNotFoundException ex2) { | ||
// Swallow - let original exception get through | ||
} | ||
} | ||
throw ex; | ||
} | ||
} | ||
|
||
public static Class<?> resolvePrimitiveClassName( String name) { | ||
Class<?> result = null; | ||
if (name != null && name.length() <= 7) { | ||
result = primitiveTypeNameMap.get(name); | ||
result = (Class<?>)primitiveTypeNameMap.get(name); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
public static ClassLoader getDefaultClassLoader() { | ||
ClassLoader cl = null; | ||
|
||
try { | ||
cl = Thread.currentThread().getContextClassLoader(); | ||
} catch (Throwable var3) { | ||
} | ||
|
||
catch (Throwable ex) { | ||
// Cannot access thread context ClassLoader - falling back... | ||
} | ||
if (cl == null) { | ||
// No thread context class loader -> use class loader of this class. | ||
cl = ClassUtil.class.getClassLoader(); | ||
if (cl == null) { | ||
// getClassLoader() returning null indicates the bootstrap ClassLoader | ||
try { | ||
cl = ClassLoader.getSystemClassLoader(); | ||
} catch (Throwable var2) { | ||
} | ||
catch (Throwable ex) { | ||
// Cannot access system ClassLoader - oh well, maybe the caller can live with null... | ||
} | ||
} | ||
} | ||
|
||
return cl; | ||
} | ||
|
||
private static void registerCommonClasses(Class<?>... commonClasses) { | ||
for (Class<?> clazz : commonClasses) { | ||
commonClassCache.put(clazz.getName(), clazz); | ||
} | ||
} | ||
|
||
static { | ||
primitiveWrapperTypeMap.put(Boolean.class, boolean.class); | ||
primitiveWrapperTypeMap.put(Byte.class, byte.class); | ||
primitiveWrapperTypeMap.put(Character.class, char.class); | ||
primitiveWrapperTypeMap.put(Double.class, double.class); | ||
primitiveWrapperTypeMap.put(Float.class, float.class); | ||
primitiveWrapperTypeMap.put(Integer.class, int.class); | ||
primitiveWrapperTypeMap.put(Long.class, long.class); | ||
primitiveWrapperTypeMap.put(Short.class, short.class); | ||
primitiveWrapperTypeMap.put(Void.class, void.class); | ||
|
||
// Map entry iteration is less expensive to initialize than forEach with lambdas | ||
for (Map.Entry<Class<?>, Class<?>> entry : primitiveWrapperTypeMap.entrySet()) { | ||
primitiveTypeToWrapperMap.put(entry.getValue(), entry.getKey()); | ||
registerCommonClasses(entry.getKey()); | ||
} | ||
|
||
Set<Class<?>> primitiveTypes = new HashSet<>(32); | ||
primitiveTypes.addAll(primitiveWrapperTypeMap.values()); | ||
Collections.addAll(primitiveTypes, boolean[].class, byte[].class, char[].class, | ||
double[].class, float[].class, int[].class, long[].class, short[].class); | ||
for (Class<?> primitiveType : primitiveTypes) { | ||
primitiveTypeNameMap.put(primitiveType.getName(), primitiveType); | ||
} | ||
|
||
registerCommonClasses(Boolean[].class, Byte[].class, Character[].class, Double[].class, | ||
Float[].class, Integer[].class, Long[].class, Short[].class); | ||
registerCommonClasses(Number.class, Number[].class, String.class, String[].class, | ||
Class.class, Class[].class, Object.class, Object[].class); | ||
registerCommonClasses(Throwable.class, Exception.class, RuntimeException.class, | ||
Error.class, StackTraceElement.class, StackTraceElement[].class); | ||
registerCommonClasses(Enum.class, Iterable.class, Iterator.class, Enumeration.class, | ||
Collection.class, List.class, Set.class, Map.class, Map.Entry.class, Optional.class); | ||
|
||
Class<?>[] javaLanguageInterfaceArray = {Serializable.class, Externalizable.class, | ||
Closeable.class, AutoCloseable.class, Cloneable.class, Comparable.class}; | ||
registerCommonClasses(javaLanguageInterfaceArray); | ||
} | ||
} |
Oops, something went wrong.