mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-24 19:31:17 +08:00
handle circle reference
This commit is contained in:
parent
3feba7fdfa
commit
ea8f305152
@ -33,7 +33,7 @@ public class Child {
|
||||
* 这是一个私有内部类
|
||||
* An private inner class
|
||||
*/
|
||||
private static class InnerChild {
|
||||
private class InnerChild {
|
||||
private String secret;
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ class DemoOmniMethodsTest {
|
||||
assertEquals("demo in batch", parent.getChildren()[1].getGrandChild().getContent());
|
||||
assertEquals("demo in batch", parent.getChildren()[2].getGrandChild().getContent());
|
||||
|
||||
// 读写私有内部类类型的成员(注意使用类型名引用类别类时,无需带外部类名)
|
||||
// 读写私有内部类类型的成员(使用类型名引用内部类时,无需带外部类名)
|
||||
assertEquals("", OmniAccessor.getFirst(parent, "subChild/secret"));
|
||||
OmniAccessor.set(parent, "{InnerChild}/secret", "inner-class secret");
|
||||
assertEquals("inner-class secret", OmniAccessor.getFirst(parent, "subChild/secret"));
|
||||
|
@ -6,10 +6,7 @@ import com.alibaba.testable.core.util.TypeUtil;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import static com.alibaba.testable.core.constant.ConstPool.SLASH;
|
||||
|
||||
@ -104,21 +101,23 @@ public class OmniAccessor {
|
||||
}
|
||||
|
||||
private static List<String> generateMemberIndex(Class<?> clazz) {
|
||||
return generateMemberIndex("", clazz);
|
||||
return generateMemberIndex(clazz, "", new HashSet<Class<?>>(6));
|
||||
}
|
||||
|
||||
private static List<String> generateMemberIndex(String basePath, Class<?> clazz) {
|
||||
private static List<String> generateMemberIndex(Class<?> clazz, String basePath, Set<Class<?>> classPool) {
|
||||
if (isAtomicType(clazz)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
classPool.add(clazz);
|
||||
List<String> paths = new ArrayList<String>();
|
||||
for (Field f : TypeUtil.getAllFields(clazz)) {
|
||||
if (!f.getName().startsWith(THIS_REF_PREFIX)) {
|
||||
if (!classPool.contains(f.getType()) && !f.getName().startsWith(THIS_REF_PREFIX)) {
|
||||
String fullPath = basePath + SLASH + toPath(f);
|
||||
paths.add(fullPath);
|
||||
paths.addAll(generateMemberIndex(fullPath, f.getType()));
|
||||
paths.addAll(generateMemberIndex(f.getType(), fullPath, classPool));
|
||||
}
|
||||
}
|
||||
classPool.remove(clazz);
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,18 @@ import com.alibaba.testable.core.model.Null;
|
||||
import com.alibaba.testable.core.util.TypeUtil;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author flin
|
||||
*/
|
||||
public class OmniConstructor {
|
||||
|
||||
private static final int INITIAL_CAPACITY = 6;
|
||||
|
||||
private OmniConstructor() {}
|
||||
|
||||
/**
|
||||
@ -20,30 +26,7 @@ public class OmniConstructor {
|
||||
* @return 返回新创建的对象
|
||||
*/
|
||||
public static <T> T newInstance(Class<T> clazz) {
|
||||
try {
|
||||
if (clazz.isPrimitive()) {
|
||||
return newPrimitive(clazz);
|
||||
} else if (clazz.isArray()) {
|
||||
return (T)newArray(clazz.getComponentType(), 0);
|
||||
} else if (clazz.isEnum()) {
|
||||
return newEnum(clazz);
|
||||
} else if (clazz.isInterface()) {
|
||||
return newInterface(clazz);
|
||||
} else if (Modifier.isAbstract(clazz.getModifiers())) {
|
||||
return newAbstractClass(clazz);
|
||||
}
|
||||
return newObject(clazz);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new ClassConstructionException("Failed to find constructor", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new ClassConstructionException("Failed to access constructor", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new ClassConstructionException("Failed to invoke constructor", e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new ClassConstructionException("Failed to complete construction", e);
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassConstructionException("Unexpected type", e);
|
||||
}
|
||||
return handleCircleReference(newInstance(clazz, new HashSet<Class<?>>(INITIAL_CAPACITY)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,27 +37,62 @@ public class OmniConstructor {
|
||||
* @return 返回新创建的对象数组
|
||||
*/
|
||||
public static <T> T[] newArray(Class<T> clazz, int size) {
|
||||
T[] array = (T[])Array.newInstance(clazz, size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Array.set(array, i, newInstance(clazz));
|
||||
}
|
||||
return array;
|
||||
return handleCircleReference(newArray(clazz, size, new HashSet<Class<?>>(INITIAL_CAPACITY)));
|
||||
}
|
||||
|
||||
private static <T> T newObject(Class<T> clazz)
|
||||
private static <T> T newInstance(Class<T> clazz, Set<Class<?>> classPool) {
|
||||
if (classPool.contains(clazz)) {
|
||||
return null;
|
||||
}
|
||||
classPool.add(clazz);
|
||||
try {
|
||||
if (clazz.isPrimitive()) {
|
||||
return newPrimitive(clazz);
|
||||
} else if (clazz.isArray()) {
|
||||
return (T)newArray(clazz.getComponentType(), 0, classPool);
|
||||
} else if (clazz.isEnum()) {
|
||||
return newEnum(clazz);
|
||||
} else if (clazz.isInterface()) {
|
||||
return newInterface(clazz);
|
||||
} else if (Modifier.isAbstract(clazz.getModifiers())) {
|
||||
return newAbstractClass(clazz);
|
||||
}
|
||||
return newObject(clazz, classPool);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new ClassConstructionException("Failed to find constructor", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new ClassConstructionException("Failed to access constructor", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new ClassConstructionException("Failed to invoke constructor", e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new ClassConstructionException("Failed to complete construction", e);
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassConstructionException("Unexpected type", e);
|
||||
} finally {
|
||||
classPool.remove(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T newObject(Class<T> clazz, Set<Class<?>> classPool)
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||
Constructor<?> constructor = getBestConstructor(clazz);
|
||||
constructor.setAccessible(true);
|
||||
Object ins = createInstance(constructor);
|
||||
Object ins = createInstance(getBestConstructor(clazz), classPool);
|
||||
for (Field f : TypeUtil.getAllFields(clazz)) {
|
||||
f.setAccessible(true);
|
||||
if (f.get(ins) == null) {
|
||||
f.set(ins, newInstance(f.getType()));
|
||||
f.set(ins, newInstance(f.getType(), classPool));
|
||||
}
|
||||
}
|
||||
return (T)ins;
|
||||
}
|
||||
|
||||
private static <T> T[] newArray(Class<T> clazz, int size, Set<Class<?>> classPool) {
|
||||
T[] array = (T[])Array.newInstance(clazz, size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Array.set(array, i, newInstance(clazz, classPool));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private static <T> T newAbstractClass(Class<T> clazz) {
|
||||
return null;
|
||||
}
|
||||
@ -110,21 +128,59 @@ public class OmniConstructor {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Object createInstance(Constructor<?> constructor)
|
||||
private static <T> T handleCircleReference(T instance) {
|
||||
try {
|
||||
if (instance.getClass().isArray()) {
|
||||
for (int i = 0; i < Array.getLength(instance); i++) {
|
||||
handleCircleReference(Array.get(instance, i), new HashMap<Class<?>, Object>(INITIAL_CAPACITY));
|
||||
}
|
||||
} else {
|
||||
handleCircleReference(instance, new HashMap<Class<?>, Object>(INITIAL_CAPACITY));
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new ClassConstructionException("Failed to access field", e);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static <T> void handleCircleReference(T instance, Map<Class<?>, Object> classPool)
|
||||
throws IllegalAccessException {
|
||||
classPool.put(instance.getClass(), instance);
|
||||
for (Field f : TypeUtil.getAllFields(instance.getClass())) {
|
||||
f.setAccessible(true);
|
||||
Object fieldIns = f.get(instance);
|
||||
Class<?> fieldType = f.getType();
|
||||
if (fieldType.isArray()) {
|
||||
for (int i = 0; i < Array.getLength(fieldIns); i++) {
|
||||
handleCircleReference(Array.get(fieldIns, i), classPool);
|
||||
}
|
||||
} else if (!fieldType.isPrimitive() && !fieldType.isEnum()) {
|
||||
if (fieldIns == null && classPool.containsKey(fieldType)) {
|
||||
f.set(instance, classPool.get(fieldType));
|
||||
} else if (fieldIns != null && !classPool.containsKey(fieldType)) {
|
||||
handleCircleReference(fieldIns, classPool);
|
||||
}
|
||||
}
|
||||
}
|
||||
classPool.remove(instance.getClass());
|
||||
}
|
||||
|
||||
private static Object createInstance(Constructor<?> constructor, Set<Class<?>> classPool)
|
||||
throws InstantiationException, IllegalAccessException, InvocationTargetException {
|
||||
constructor.setAccessible(true);
|
||||
Class<?>[] types = constructor.getParameterTypes();
|
||||
if (types.length == 1 && types[0].equals(Null.class)) {
|
||||
return constructor.newInstance(new Null());
|
||||
} else {
|
||||
Object[] args = new Object[types.length];
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
args[i] = newInstance(types[i]);
|
||||
args[i] = newInstance(types[i], classPool);
|
||||
}
|
||||
return constructor.newInstance(args);
|
||||
}
|
||||
}
|
||||
|
||||
private static Constructor<?> getBestConstructor(Class<?> clazz) throws NoSuchMethodException {
|
||||
private static Constructor<?> getBestConstructor(Class<?> clazz) {
|
||||
Constructor<?> bestConstructor = null;
|
||||
int minimalParametersSize = 999;
|
||||
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
|
||||
|
Loading…
Reference in New Issue
Block a user