convert query path to match pattern

This commit is contained in:
金戟 2021-03-16 11:50:22 +08:00
parent bdd16c0948
commit a5038320b4
5 changed files with 104 additions and 22 deletions

View File

@ -1,6 +1,7 @@
package com.alibaba.testable.core.tool; package com.alibaba.testable.core.tool;
import com.alibaba.testable.core.util.UnnullableMap; import com.alibaba.testable.core.util.FixSizeMap;
import com.sun.deploy.util.StringUtils;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
@ -14,7 +15,7 @@ import static com.alibaba.testable.core.constant.ConstPool.SLASH;
*/ */
public class OmniAccessor { public class OmniAccessor {
private static final UnnullableMap<Class<?>, List<String>> MEMBER_INDEXES = UnnullableMap.of(new ArrayList<String>()); private static final FixSizeMap<Class<?>, List<String>> MEMBER_INDEXES = new FixSizeMap<Class<?>, List<String>>(30);
private static final String THIS_REF_PREFIX = "this$"; private static final String THIS_REF_PREFIX = "this$";
/** /**
@ -54,16 +55,19 @@ public class OmniAccessor {
* @param target 目标对象 * @param target 目标对象
* @param queryPath 搜索路径 * @param queryPath 搜索路径
* @param value 新的值 * @param value 新的值
* @return 实际影响的成员个数
*/ */
public static void set(Object target, String queryPath, Object value) { public static int set(Object target, String queryPath, Object value) {
int count = 0;
for (String memberPath : MEMBER_INDEXES.getOrElse(target.getClass(), generateMemberIndex(target.getClass()))) { for (String memberPath : MEMBER_INDEXES.getOrElse(target.getClass(), generateMemberIndex(target.getClass()))) {
if (memberPath.matches(toPattern(queryPath))) { if (memberPath.matches(toPattern(queryPath))) {
Object parent = getByPath(target, toParent(memberPath)); Object parent = getByPath(target, toParent(memberPath));
if (parent != null) { if (parent != null && setByPath(parent, toChild(memberPath), value)) {
setByPath(parent, toChild(memberPath), value); count++;
} }
} }
} }
return count;
} }
private static List<String> generateMemberIndex(Class<?> clazz) { private static List<String> generateMemberIndex(Class<?> clazz) {
@ -84,9 +88,10 @@ public class OmniAccessor {
} }
private static List<Field> getAllFields(Class<?> clazz) { private static List<Field> getAllFields(Class<?> clazz) {
List<Field> fields = new ArrayList<Field>(Arrays.asList(clazz.getDeclaredFields())); Class<?> rawClass = clazz.isArray() ? clazz.getComponentType() : clazz;
if (clazz.getSuperclass() != null) { List<Field> fields = new ArrayList<Field>(Arrays.asList(rawClass.getDeclaredFields()));
fields.addAll(getAllFields(clazz.getSuperclass())); if (rawClass.getSuperclass() != null) {
fields.addAll(getAllFields(rawClass.getSuperclass()));
} }
return fields; return fields;
} }
@ -96,7 +101,23 @@ public class OmniAccessor {
} }
private static String toPattern(String queryPath) { private static String toPattern(String queryPath) {
return ""; String[] querySegments = queryPath.split(SLASH);
String[] patternSegments = new String[querySegments.length];
for (int i = 0; i < querySegments.length; i++) {
patternSegments[i] = toSinglePattern(querySegments[i]);
}
return StringUtils.join(Arrays.asList(patternSegments), SLASH);
}
private static String toSinglePattern(String querySegment) {
if (querySegment.isEmpty()) {
return "";
} else if (querySegment.startsWith("{")) {
return "[^{]+" + querySegment.replace("{", "\\{").replace("}", "\\}")
.replace("[", "\\[").replace("]", "\\]");
} else {
return querySegment + "\\{[^}]+\\}";
}
} }
private static String toChild(String memberPath) { private static String toChild(String memberPath) {
@ -111,8 +132,8 @@ public class OmniAccessor {
return null; return null;
} }
private static void setByPath(Object target, String memberPath, Object value) { private static boolean setByPath(Object target, String memberPath, Object value) {
return true;
} }
} }

View File

@ -0,0 +1,45 @@
package com.alibaba.testable.core.util;
import com.sun.tools.javac.util.ListBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
/**
* @author flin
*/
public class FixSizeMap<K, V> {
private final int capacity;
private final Map<K, V> content;
private final Queue<K> order;
public FixSizeMap(int size) {
this.capacity = size;
this.content = new HashMap<K, V>(size);
this.order = new ListBuffer<K>();
}
public V get(K key) {
return content.get(key);
}
public void put(K key, V value) {
if (order.size() >= capacity) {
content.remove(order.poll());
}
order.add(key);
content.put(key, value);
}
public V getOrElse(K key, V elseValue) {
V value = get(key);
if (value == null) {
value = elseValue;
put(key, value);
}
return value;
}
}

View File

@ -2,9 +2,9 @@ package com.alibaba.testable.core.tool;
public class DemoChild { public class DemoChild {
public DemoGrandChild gc1; public DemoGrandChild gc;
private DemoGrandChild gc2; private DemoGrandChild[] gcs;
public class SubChild { public class SubChild {
private DemoGrandChild gc; private DemoGrandChild gc;

View File

@ -2,9 +2,9 @@ package com.alibaba.testable.core.tool;
public class DemoParent { public class DemoParent {
private DemoChild c1; private DemoChild c;
public DemoChild c2; public DemoChild[] cs;
public DemoChild.SubChild sc; public DemoChild.SubChild sc;

View File

@ -13,12 +13,12 @@ class OmniAccessorTest {
void should_generate_member_index() { void should_generate_member_index() {
List<String> index = PrivateAccessor.invokeStatic(OmniAccessor.class, "generateMemberIndex", DemoParent.class); List<String> index = PrivateAccessor.invokeStatic(OmniAccessor.class, "generateMemberIndex", DemoParent.class);
assertEquals(10, index.size()); assertEquals(10, index.size());
assertEquals("/c1{DemoChild}", index.get(0)); assertEquals("/c{DemoChild}", index.get(0));
assertEquals("/c1{DemoChild}/gc1{DemoGrandChild}", index.get(1)); assertEquals("/c{DemoChild}/gc{DemoGrandChild}", index.get(1));
assertEquals("/c1{DemoChild}/gc2{DemoGrandChild}", index.get(2)); assertEquals("/c{DemoChild}/gcs{DemoGrandChild[]}", index.get(2));
assertEquals("/c2{DemoChild}", index.get(3)); assertEquals("/cs{DemoChild[]}", index.get(3));
assertEquals("/c2{DemoChild}/gc1{DemoGrandChild}", index.get(4)); assertEquals("/cs{DemoChild[]}/gc{DemoGrandChild}", index.get(4));
assertEquals("/c2{DemoChild}/gc2{DemoGrandChild}", index.get(5)); assertEquals("/cs{DemoChild[]}/gcs{DemoGrandChild[]}", index.get(5));
assertEquals("/sc{SubChild}", index.get(6)); assertEquals("/sc{SubChild}", index.get(6));
assertEquals("/sc{SubChild}/gc{DemoGrandChild}", index.get(7)); assertEquals("/sc{SubChild}/gc{DemoGrandChild}", index.get(7));
assertEquals("/ssc{StaticSubChild}", index.get(8)); assertEquals("/ssc{StaticSubChild}", index.get(8));
@ -26,8 +26,24 @@ class OmniAccessorTest {
} }
@Test @Test
void should_to_pattern() { void should_match_pattern() {
assertTrue("abc{Abc}".matches("abc\\{[^}]+\\}"));
assertTrue("abc{Abc}".matches("[^{]+\\{Abc\\}"));
assertTrue("abc{Abc[]}/xyz{Xyz[]}".matches("[^{]+\\{Abc\\[\\]\\}/[^{]+\\{Xyz\\[\\]\\}"));
assertTrue("abc{Abc}/xyz{Xyz}/demo{Demo}".matches("abc\\{[^}]+\\}/[^{]+\\{Xyz\\}/demo\\{[^}]+\\}"));
}
@Test
void should_to_pattern() {
assertEquals("", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", ""));
assertEquals("abc\\{[^}]+\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc"));
assertEquals("[^{]+\\{Abc\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "{Abc}"));
assertEquals("abc\\{[^}]+\\}/xyz\\{[^}]+\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc/xyz"));
assertEquals("[^{]+\\{Abc\\}/xyz\\{[^}]+\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "{Abc}/xyz"));
assertEquals("abc\\{[^}]+\\}/[^{]+\\{Xyz\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc/{Xyz}"));
assertEquals("[^{]+\\{Abc\\}/[^{]+\\{Xyz\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "{Abc}/{Xyz}"));
assertEquals("[^{]+\\{Abc\\[\\]\\}/[^{]+\\{Xyz\\[\\]\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "{Abc[]}/{Xyz[]}"));
assertEquals("abc\\{[^}]+\\}/[^{]+\\{Xyz\\}/demo\\{[^}]+\\}", PrivateAccessor.<String>invokeStatic(OmniAccessor.class, "toPattern", "abc/{Xyz}/demo"));
} }
@Test @Test