mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-25 11:51:15 +08:00
generate member index of specified class
This commit is contained in:
parent
dce1accafb
commit
bdd16c0948
@ -5,11 +5,6 @@ package com.alibaba.testable.agent.constant;
|
||||
*/
|
||||
public class ConstPool {
|
||||
|
||||
public static final String DOT = ".";
|
||||
public static final String SLASH = "/";
|
||||
public static final String DOLLAR = "$";
|
||||
public static final String UNDERLINE = "_";
|
||||
|
||||
public static final String FIELD_TARGET_METHOD = "targetMethod";
|
||||
public static final String FIELD_TARGET_CLASS = "targetClass";
|
||||
public static final String FIELD_SCOPE = "scope";
|
||||
|
@ -22,7 +22,8 @@ import java.lang.instrument.ClassFileTransformer;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.List;
|
||||
|
||||
import static com.alibaba.testable.agent.constant.ConstPool.*;
|
||||
import static com.alibaba.testable.agent.constant.ConstPool.KOTLIN_POSTFIX_COMPANION;
|
||||
import static com.alibaba.testable.core.constant.ConstPool.*;
|
||||
import static com.alibaba.testable.agent.util.ClassUtil.toJavaStyleClassName;
|
||||
import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
|
@ -11,8 +11,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.alibaba.testable.agent.constant.ByteCodeConst.*;
|
||||
import static com.alibaba.testable.core.constant.ConstPool.MOCK_POSTFIX;
|
||||
import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX;
|
||||
import static com.alibaba.testable.core.constant.ConstPool.*;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
@ -180,7 +179,7 @@ public class ClassUtil {
|
||||
* @return converted name
|
||||
*/
|
||||
public static String toDotSeparatedName(String name) {
|
||||
return name.replace(ConstPool.SLASH, ConstPool.DOT);
|
||||
return name.replace(SLASH, DOT);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,7 +188,7 @@ public class ClassUtil {
|
||||
* @return converted name
|
||||
*/
|
||||
public static String toSlashSeparatedName(String name) {
|
||||
return name.replace(ConstPool.DOT, ConstPool.SLASH);
|
||||
return name.replace(DOT, SLASH);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,4 +13,12 @@ public class ConstPool {
|
||||
public static final String TEST_POSTFIX = "Test";
|
||||
public static final String MOCK_POSTFIX = "Mock";
|
||||
|
||||
/**
|
||||
* Common symbols
|
||||
*/
|
||||
public static final String DOT = ".";
|
||||
public static final String SLASH = "/";
|
||||
public static final String DOLLAR = "$";
|
||||
public static final String UNDERLINE = "_";
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,118 @@
|
||||
package com.alibaba.testable.core.tool;
|
||||
|
||||
import com.alibaba.testable.core.util.UnnullableMap;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static com.alibaba.testable.core.constant.ConstPool.SLASH;
|
||||
|
||||
/**
|
||||
* @author flin
|
||||
*/
|
||||
public class OmniAccessor {
|
||||
|
||||
private static final UnnullableMap<Class<?>, List<String>> MEMBER_INDEXES = UnnullableMap.of(new ArrayList<String>());
|
||||
private static final String THIS_REF_PREFIX = "this$";
|
||||
|
||||
/**
|
||||
* 获取第一个符合搜索路径的成员
|
||||
* @param target 目标对象
|
||||
* @param queryPath 搜索路径
|
||||
* @param clazz 目标成员类型
|
||||
* @return 返回目标成员,若不存在则返回null
|
||||
*/
|
||||
public static <T> T getFirst(Object target, String queryPath, Class<T> clazz) {
|
||||
List<T> values = get(target, queryPath, clazz);
|
||||
return values.isEmpty() ? null : values.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有符合搜索路径的成员
|
||||
* @param target 目标对象
|
||||
* @param queryPath 搜索路径
|
||||
* @param clazz 目标成员类型
|
||||
* @return 返回所有匹配的成员
|
||||
*/
|
||||
public static <T> List<T> get(Object target, String queryPath, Class<T> clazz) {
|
||||
List<T> values = new ArrayList<T>();
|
||||
for (String memberPath : MEMBER_INDEXES.getOrElse(target.getClass(), generateMemberIndex(target.getClass()))) {
|
||||
if (memberPath.matches(toPattern(queryPath))) {
|
||||
T val = (T)getByPath(target, memberPath);
|
||||
if (val != null) {
|
||||
values.add(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为符合搜索路径的成员赋值
|
||||
* @param target 目标对象
|
||||
* @param queryPath 搜索路径
|
||||
* @param value 新的值
|
||||
*/
|
||||
public static void set(Object target, String queryPath, Object value) {
|
||||
for (String memberPath : MEMBER_INDEXES.getOrElse(target.getClass(), generateMemberIndex(target.getClass()))) {
|
||||
if (memberPath.matches(toPattern(queryPath))) {
|
||||
Object parent = getByPath(target, toParent(memberPath));
|
||||
if (parent != null) {
|
||||
setByPath(parent, toChild(memberPath), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> generateMemberIndex(Class<?> clazz) {
|
||||
return generateMemberIndex("", clazz);
|
||||
}
|
||||
|
||||
private static List<String> generateMemberIndex(String basePath, Class<?> clazz) {
|
||||
List<Field> fields = getAllFields(clazz);
|
||||
List<String> paths = new ArrayList<String>();
|
||||
for (Field f : fields) {
|
||||
if (!f.getName().startsWith(THIS_REF_PREFIX)) {
|
||||
String fullPath = basePath + SLASH + toPath(f);
|
||||
paths.add(fullPath);
|
||||
paths.addAll(generateMemberIndex(fullPath, f.getType()));
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
private static List<Field> getAllFields(Class<?> clazz) {
|
||||
List<Field> fields = new ArrayList<Field>(Arrays.asList(clazz.getDeclaredFields()));
|
||||
if (clazz.getSuperclass() != null) {
|
||||
fields.addAll(getAllFields(clazz.getSuperclass()));
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
private static String toPath(Field field) {
|
||||
return field.getName() + "{" + field.getType().getSimpleName() + "}";
|
||||
}
|
||||
|
||||
private static String toPattern(String queryPath) {
|
||||
return "";
|
||||
}
|
||||
|
||||
private static String toChild(String memberPath) {
|
||||
return memberPath.contains(SLASH) ? memberPath.substring(memberPath.lastIndexOf(SLASH) + 1) : memberPath;
|
||||
}
|
||||
|
||||
private static String toParent(String memberPath) {
|
||||
return memberPath.contains(SLASH) ? memberPath.substring(0, memberPath.lastIndexOf(SLASH)) : "";
|
||||
}
|
||||
|
||||
private static Object getByPath(Object target, String memberPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void setByPath(Object target, String memberPath, Object value) {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.alibaba.testable.core.tool;
|
||||
|
||||
/**
|
||||
* @author flin
|
||||
*/
|
||||
public class OmniConstructor {
|
||||
|
||||
public static <T> T newInstance(Class<T> clazz) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> void appendInstance(Object target, Class<T> clazz) {
|
||||
return;
|
||||
}
|
||||
|
||||
public static <T> void appendInstance(Object target, int count, Class<T> clazz) {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
@ -32,4 +32,14 @@ public class UnnullableMap<K, V> extends HashMap<K, V> {
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public V getOrElse(Object key, V elseValue) {
|
||||
V value = super.get(key);
|
||||
if (value == null) {
|
||||
value = elseValue;
|
||||
super.put((K)key, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
package com.alibaba.testable.core.tool;
|
||||
|
||||
public class DemoChild {
|
||||
|
||||
public DemoGrandChild gc1;
|
||||
|
||||
private DemoGrandChild gc2;
|
||||
|
||||
public class SubChild {
|
||||
private DemoGrandChild gc;
|
||||
}
|
||||
|
||||
public static class StaticSubChild {
|
||||
private DemoGrandChild gc;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package com.alibaba.testable.core.tool;
|
||||
|
||||
public class DemoGrandChild {
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.alibaba.testable.core.tool;
|
||||
|
||||
public class DemoParent {
|
||||
|
||||
private DemoChild c1;
|
||||
|
||||
public DemoChild c2;
|
||||
|
||||
public DemoChild.SubChild sc;
|
||||
|
||||
public DemoChild.StaticSubChild ssc;
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package com.alibaba.testable.core.tool;
|
||||
|
||||
import com.alibaba.testable.core.accessor.PrivateAccessor;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class OmniAccessorTest {
|
||||
|
||||
@Test
|
||||
void should_generate_member_index() {
|
||||
List<String> index = PrivateAccessor.invokeStatic(OmniAccessor.class, "generateMemberIndex", DemoParent.class);
|
||||
assertEquals(10, index.size());
|
||||
assertEquals("/c1{DemoChild}", index.get(0));
|
||||
assertEquals("/c1{DemoChild}/gc1{DemoGrandChild}", index.get(1));
|
||||
assertEquals("/c1{DemoChild}/gc2{DemoGrandChild}", index.get(2));
|
||||
assertEquals("/c2{DemoChild}", index.get(3));
|
||||
assertEquals("/c2{DemoChild}/gc1{DemoGrandChild}", index.get(4));
|
||||
assertEquals("/c2{DemoChild}/gc2{DemoGrandChild}", index.get(5));
|
||||
assertEquals("/sc{SubChild}", index.get(6));
|
||||
assertEquals("/sc{SubChild}/gc{DemoGrandChild}", index.get(7));
|
||||
assertEquals("/ssc{StaticSubChild}", index.get(8));
|
||||
assertEquals("/ssc{StaticSubChild}/gc{DemoGrandChild}", index.get(9));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_to_pattern() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_to_parent() {
|
||||
assertEquals("", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toParent", ""));
|
||||
assertEquals("", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toParent", "abc"));
|
||||
assertEquals("abc", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toParent", "abc/xyz"));
|
||||
assertEquals("abc/def", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toParent", "abc/def/xyz"));
|
||||
assertEquals("/abc/def", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toParent", "/abc/def/xyz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_to_child() {
|
||||
assertEquals("", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toChild", ""));
|
||||
assertEquals("abc", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toChild", "abc"));
|
||||
assertEquals("xyz", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toChild", "abc/xyz"));
|
||||
assertEquals("xyz", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toChild", "abc/def/xyz"));
|
||||
assertEquals("xyz", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toChild", "/abc/def/xyz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_get_by_path() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_set_by_path() {
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user