From 328296ae3a61f00ea9cbd887431560d819c4c993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=88=9F?= Date: Tue, 16 Mar 2021 14:31:37 +0800 Subject: [PATCH] complete get and set by path of common class --- .../testable/core/tool/OmniAccessor.java | 82 +++++++++++++++---- .../testable/core/tool/DemoGrandChild.java | 6 ++ .../testable/core/tool/DemoParent.java | 4 +- .../testable/core/tool/OmniAccessorTest.java | 43 +++++++--- 4 files changed, 107 insertions(+), 28 deletions(-) diff --git a/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java index 5f49b84..071fc7e 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java @@ -1,6 +1,7 @@ package com.alibaba.testable.core.tool; import com.alibaba.testable.core.util.FixSizeMap; +import com.alibaba.testable.core.util.TypeUtil; import com.sun.deploy.util.StringUtils; import java.lang.reflect.Field; @@ -17,6 +18,13 @@ public class OmniAccessor { private static final FixSizeMap, List> MEMBER_INDEXES = new FixSizeMap, List>(30); private static final String THIS_REF_PREFIX = "this$"; + private static final String REGEX_ANY_CLASS = "\\{[^}]+\\}"; + private static final String REGEX_ANY_NAME = "[^{]+"; + private static final String BRACE_START = "{"; + private static final String ESCAPE = "\\"; + private static final String BRACE_END = "}"; + private static final String BRACKET_START = "["; + private static final String BRACKET_END = "]"; /** * 获取第一个符合搜索路径的成员 @@ -41,9 +49,15 @@ public class OmniAccessor { List values = new ArrayList(); 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); + try { + T val = (T)getByPath(target, memberPath, queryPath); + if (val != null) { + values.add(val); + } + } catch (NoSuchFieldException e) { + // continue + } catch (IllegalAccessException e) { + // continue } } } @@ -61,9 +75,16 @@ public class OmniAccessor { int count = 0; 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)) { - count++; + try { + Object parent = getByPath(target, toParent(memberPath), toParent(queryPath)); + if (parent != null) { + setByPathSegment(parent, toChild(memberPath), toChild(queryPath), value); + count++; + } + } catch (NoSuchFieldException e) { + // continue + } catch (IllegalAccessException e) { + // continue } } } @@ -97,7 +118,7 @@ public class OmniAccessor { } private static String toPath(Field field) { - return field.getName() + "{" + field.getType().getSimpleName() + "}"; + return field.getName() + BRACE_START + field.getType().getSimpleName() + BRACE_END; } private static String toPattern(String queryPath) { @@ -110,13 +131,18 @@ public class OmniAccessor { } private static String toSinglePattern(String querySegment) { + if (querySegment.endsWith(BRACKET_END)) { + querySegment = querySegment.substring(0, querySegment.lastIndexOf(BRACKET_START)); + } if (querySegment.isEmpty()) { return ""; - } else if (querySegment.startsWith("{")) { - return "[^{]+" + querySegment.replace("{", "\\{").replace("}", "\\}") - .replace("[", "\\[").replace("]", "\\]"); + } else if (querySegment.startsWith(BRACE_START)) { + return REGEX_ANY_NAME + querySegment.replace(BRACE_START, ESCAPE + BRACE_START) + .replace(BRACE_END, ESCAPE + BRACE_END) + .replace(BRACKET_START, ESCAPE + BRACKET_START) + .replace(BRACKET_END, ESCAPE + BRACKET_END); } else { - return querySegment + "\\{[^}]+\\}"; + return querySegment + REGEX_ANY_CLASS; } } @@ -128,12 +154,38 @@ public class OmniAccessor { return memberPath.contains(SLASH) ? memberPath.substring(0, memberPath.lastIndexOf(SLASH)) : ""; } - private static Object getByPath(Object target, String memberPath) { - return null; + private static int extraIndexFromQuery(String query) { + return query.endsWith(BRACKET_END) + ? Integer.parseInt(query.substring(query.lastIndexOf(BRACKET_START) + 1), query.length() - 1) + : -1; } - private static boolean setByPath(Object target, String memberPath, Object value) { - return true; + private static Object getByPath(Object target, String memberPath, String queryPath) + throws NoSuchFieldException, IllegalAccessException { + String[] memberSegments = memberPath.split(SLASH); + String[] querySegments = queryPath.split(SLASH); + Object obj = target; + String name; + int nth; + Field field; + assert memberSegments.length == querySegments.length; + for (int i = 0; i < memberSegments.length; i++) { + name = memberSegments[i].substring(0, memberSegments[i].indexOf(BRACE_START)); + nth = extraIndexFromQuery(querySegments[i]); + field = TypeUtil.getFieldByName(obj.getClass(), name); + field.setAccessible(true); + obj = field.get(obj); + } + return obj; + } + + private static void setByPathSegment(Object target, String memberSegment, String querySegment, Object value) + throws IllegalAccessException { + String name = memberSegment.substring(0, memberSegment.indexOf(BRACE_START)); + int nth = extraIndexFromQuery(querySegment); + Field field = TypeUtil.getFieldByName(target.getClass(), name); + field.setAccessible(true); + field.set(target, value); } } diff --git a/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoGrandChild.java b/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoGrandChild.java index d084d7d..7bd709f 100644 --- a/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoGrandChild.java +++ b/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoGrandChild.java @@ -1,4 +1,10 @@ package com.alibaba.testable.core.tool; public class DemoGrandChild { + + private int i; + + public DemoGrandChild(int i) { + this.i = i; + } } diff --git a/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoParent.java b/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoParent.java index 873fb48..62c9396 100644 --- a/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoParent.java +++ b/testable-core/src/test/java/com/alibaba/testable/core/tool/DemoParent.java @@ -2,9 +2,9 @@ package com.alibaba.testable.core.tool; public class DemoParent { - private DemoChild c; + public DemoChild c; - public DemoChild[] cs; + private DemoChild[] cs; public DemoChild.SubChild sc; diff --git a/testable-core/src/test/java/com/alibaba/testable/core/tool/OmniAccessorTest.java b/testable-core/src/test/java/com/alibaba/testable/core/tool/OmniAccessorTest.java index 5005802..546df63 100644 --- a/testable-core/src/test/java/com/alibaba/testable/core/tool/OmniAccessorTest.java +++ b/testable-core/src/test/java/com/alibaba/testable/core/tool/OmniAccessorTest.java @@ -12,17 +12,23 @@ class OmniAccessorTest { @Test void should_generate_member_index() { List index = PrivateAccessor.invokeStatic(OmniAccessor.class, "generateMemberIndex", DemoParent.class); - assertEquals(10, index.size()); + assertEquals(16, index.size()); assertEquals("/c{DemoChild}", index.get(0)); assertEquals("/c{DemoChild}/gc{DemoGrandChild}", index.get(1)); - assertEquals("/c{DemoChild}/gcs{DemoGrandChild[]}", index.get(2)); - assertEquals("/cs{DemoChild[]}", index.get(3)); - assertEquals("/cs{DemoChild[]}/gc{DemoGrandChild}", index.get(4)); - assertEquals("/cs{DemoChild[]}/gcs{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)); + assertEquals("/c{DemoChild}/gc{DemoGrandChild}/i{int}", index.get(2)); + assertEquals("/c{DemoChild}/gcs{DemoGrandChild[]}", index.get(3)); + assertEquals("/c{DemoChild}/gcs{DemoGrandChild[]}/i{int}", index.get(4)); + assertEquals("/cs{DemoChild[]}", index.get(5)); + assertEquals("/cs{DemoChild[]}/gc{DemoGrandChild}", index.get(6)); + assertEquals("/cs{DemoChild[]}/gc{DemoGrandChild}/i{int}", index.get(7)); + assertEquals("/cs{DemoChild[]}/gcs{DemoGrandChild[]}", index.get(8)); + assertEquals("/cs{DemoChild[]}/gcs{DemoGrandChild[]}/i{int}", index.get(9)); + assertEquals("/sc{SubChild}", index.get(10)); + assertEquals("/sc{SubChild}/gc{DemoGrandChild}", index.get(11)); + assertEquals("/sc{SubChild}/gc{DemoGrandChild}/i{int}", index.get(12)); + assertEquals("/ssc{StaticSubChild}", index.get(13)); + assertEquals("/ssc{StaticSubChild}/gc{DemoGrandChild}", index.get(14)); + assertEquals("/ssc{StaticSubChild}/gc{DemoGrandChild}/i{int}", index.get(15)); } @Test @@ -43,6 +49,7 @@ class OmniAccessorTest { assertEquals("abc\\{[^}]+\\}/[^{]+\\{Xyz\\}", PrivateAccessor.invokeStatic(OmniAccessor.class, "toPattern", "abc/{Xyz}")); assertEquals("[^{]+\\{Abc\\}/[^{]+\\{Xyz\\}", PrivateAccessor.invokeStatic(OmniAccessor.class, "toPattern", "{Abc}/{Xyz}")); assertEquals("[^{]+\\{Abc\\[\\]\\}/[^{]+\\{Xyz\\[\\]\\}", PrivateAccessor.invokeStatic(OmniAccessor.class, "toPattern", "{Abc[]}/{Xyz[]}")); + assertEquals("abc\\{[^}]+\\}/[^{]+\\{Xyz\\[\\]\\}", PrivateAccessor.invokeStatic(OmniAccessor.class, "toPattern", "abc[1]/{Xyz[]}[2]")); assertEquals("abc\\{[^}]+\\}/[^{]+\\{Xyz\\}/demo\\{[^}]+\\}", PrivateAccessor.invokeStatic(OmniAccessor.class, "toPattern", "abc/{Xyz}/demo")); } @@ -66,12 +73,26 @@ class OmniAccessorTest { @Test void should_get_by_path() { - + DemoParent parent = prepareDemoObject(); + Object obj = PrivateAccessor.invokeStatic(OmniAccessor.class, "getByPath", parent, "c{DemoChild}/gc{DemoGrandChild}", "c/gc"); + assertTrue(obj instanceof DemoGrandChild); + assertEquals(1, PrivateAccessor.get(obj, "i")); } @Test - void should_set_by_path() { + void should_set_by_path_segment() { + DemoParent parent = prepareDemoObject(); + PrivateAccessor.invokeStatic(OmniAccessor.class, "setByPathSegment", parent.c, "gc{DemoGrandChild}", "gc", new DemoGrandChild(2)); + assertEquals(2, PrivateAccessor.get(parent.c.gc, "i")); + } + private DemoParent prepareDemoObject() { + DemoParent parent = new DemoParent(); + DemoChild child = new DemoChild(); + DemoGrandChild grandChild = new DemoGrandChild(1); + PrivateAccessor.set(parent, "c", child); + PrivateAccessor.set(child, "gc", grandChild); + return parent; } }