Procházet zdrojové kódy

WalkerRules: better defaults, value formatter

Ranides Atterwim před 5 roky
rodič
revize
baa9406452

+ 2 - 0
assira/src/main/java/net/ranides/assira/reflection/impl/RClass.java

@@ -208,6 +208,8 @@ public class RClass<T> extends AClass<T> {
     }
     
     private CQuery<IField> getRFields(AHints hints) {
+        // @todo (reflective #3) return very synthetic and magical field "length" for arrays
+        // at this moment we can't read array's length for example in ResolveModel. It is quite sad.
         IContext c = context();
         if(hints.hintDeclared()) {
             return CQuery.from()

+ 12 - 1
assira/src/main/java/net/ranides/assira/reflection/walker/ObjectWalker.java

@@ -71,6 +71,8 @@ public class ObjectWalker {
         private final LazyFunction<String> namePath = LazyReference.concurrent();
         private final LazyFunction<String> idPath = LazyReference.concurrent();
 
+        private String text;
+
         public CAbstract(ObjectContext<?> parent, IField field, Object value) {
             this.field = Optional.of(field);
             this.parent = parent;
@@ -80,7 +82,6 @@ public class ObjectWalker {
             this.actual = IClass.typefor(value);
             this.id = counter++;
             this.depth = (parent == null) ? 0 : parent.depth() + 1;
-
         }
 
         public CAbstract(ObjectContext<?> parent, String name, IClass<?> declared, Object value) {
@@ -182,6 +183,16 @@ public class ObjectWalker {
             return (T)value;
         }
 
+        @Override
+        public void text(String value) {
+            this.text = value;
+        }
+
+        @Override
+        public String text() {
+            return ValueUtils.or(text, String.valueOf(value));
+        }
+
         @Override
         public long id() {
             return id;

+ 4 - 0
assira/src/main/java/net/ranides/assira/reflection/walker/WalkerContexts.java

@@ -29,6 +29,10 @@ public class WalkerContexts {
 
         T value();
 
+        void text(String value);
+
+        String text();
+
         long id();
 
         long ref();

+ 58 - 35
assira/src/main/java/net/ranides/assira/reflection/walker/WalkerRules.java

@@ -4,8 +4,10 @@ import lombok.Data;
 
 import net.ranides.assira.reflection.*;
 import net.ranides.assira.reflection.walker.ObjectVisitor.PrintVisitor;
+import net.ranides.assira.reflection.walker.WalkerContexts.ArrayContext;
 import net.ranides.assira.reflection.walker.WalkerContexts.ObjectContext;
 import net.ranides.assira.text.Charsets;
+import net.ranides.assira.text.FormatHex;
 import net.ranides.assira.text.ResolveFormat;
 
 import javax.xml.datatype.XMLGregorianCalendar;
@@ -20,7 +22,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
-import java.util.function.Predicate;
+import java.util.function.Function;
 
 public class WalkerRules {
 
@@ -33,15 +35,26 @@ public class WalkerRules {
             .term("{id,text,4}.{namePath,text,-50} ...")
         .rule()
             .match(ObjectContext::isNull)
-            .term("{id,text,4}.{namePath,text,-50} = <null>")
+            .term("{id,text,4}.{namePath,text,-50} {typeName} = <null>")
+        .rule()
+            .match(String.class)
+            .term("{id,text,4}.{namePath,text,-50} {actual.shortname} = '{value}'")
+        .rule()
+            .match(byte[].class)
+            .text(ctx -> FormatHex.format(ctx.value()))
+            .term("{id,text,4}.{namePath,text,-50} {actual.shortname} = '{text}'")
+        .rule()
+            .match(char[].class)
+            .text(ctx -> String.valueOf(ctx.value()))
+            .term("{id,text,4}.{namePath,text,-50} {actual.shortname} = '{text}'")
+        .rule()
+            .match(ArrayContext.class::isInstance)
+            .hint("{id,text,4}.{namePath,text,-50} {actual.shortname}.size = {size}")
         .rule()
             .match(List.class)
             .match(Collection.class)
             .match(Map.class)
-            .hint("{id,text,4}.{namePath,text,-50} {typeName}")
-        .rule()
-            .match(String.class)
-            .term("{id,text,4}.{namePath,text,-50} {actual.shortname} = '{value}'")
+            .hint("{id,text,4}.{namePath,text,-50} {typeName}.size = {size}")
         .rule()
             .match(java.util.Date.class)
             .match(java.sql.Date.class)
@@ -65,7 +78,12 @@ public class WalkerRules {
         return new PrintVisitor(new PrintWriter(target)){
             @Override
             public boolean visitObject(ObjectContext<?> context) {
-                return apply(this, context);
+                for (RuleEntry rule : rules) {
+                    if(rule.test(context)) {
+                        return rule.consume(this, context);
+                    }
+                }
+                return true;
             }
         };
     }
@@ -79,29 +97,12 @@ public class WalkerRules {
         return new RuleBuilderRoot();
     }
 
-    public <U> WalkerRules rule(RulePredicate<?> predicate, RuleHandler<U> handler) {
-        rules.add(new RuleEntry(predicate, handler));
-        return this;
-    }
-
-    public <U> WalkerRules rule(List<RulePredicate<?>> predicates, RuleHandler<U> handler) {
-        predicates.forEach(predicate -> rule(predicate, handler));
-        return this;
-    }
-
-    public boolean apply(PrintVisitor visitor, ObjectContext<?> ctx) {
-        for (RuleEntry rule : rules) {
-            if(rule.test(ctx)) {
-                return rule.consume(visitor, ctx);
-            }
-        }
-        return true;
-    }
-
     public abstract class RuleBuilder<F> {
 
         protected final List<RulePredicate<?>> predicates;
 
+        protected Consumer<ObjectContext<F>> formatter = x -> { };
+
         protected RuleBuilder(List<RulePredicate<?>> predicates) {
             this.predicates = predicates;
         }
@@ -116,11 +117,7 @@ public class WalkerRules {
         }
 
         public WalkerRules hint(BiConsumer<PrintVisitor, ObjectContext<F>> handler) {
-            RuleHandler<F> action = (target, ctx) -> {
-                handler.accept(target, ctx);
-                return true;
-            };
-            return rule(predicates, action);
+            return rule(predicates, asHandler(true, handler));
         }
 
         public WalkerRules term(String text) {
@@ -133,11 +130,20 @@ public class WalkerRules {
         }
 
         public WalkerRules term(BiConsumer<PrintVisitor, ObjectContext<F>> handler) {
-            RuleHandler<F> action = (target, ctx) -> {
+            return rule(predicates, asHandler(false, handler));
+        }
+
+        private RuleHandler<F> asHandler(boolean follow, BiConsumer<PrintVisitor, ObjectContext<F>> handler) {
+            return (target, ctx) -> {
+                formatter.accept(ctx);
                 handler.accept(target, ctx);
-                return false;
+                return follow;
             };
-            return rule(predicates, action);
+        }
+
+        private <U> WalkerRules rule(List<RulePredicate<?>> predicates, RuleHandler<U> handler) {
+            predicates.forEach(predicate -> rules.add(new RuleEntry(predicate, handler)));
+            return WalkerRules.this;
         }
 
     }
@@ -148,6 +154,11 @@ public class WalkerRules {
             super(new ArrayList<>());
         }
 
+        public RuleBuilderRoot text(Function<ObjectContext<Object>, String> formatter) {
+            this.formatter = ctx -> ctx.text(formatter.apply(ctx));
+            return this;
+        }
+
         public <T> RuleBuilderType<T> match(Class<T> type) {
             return match(IClass.typeinfo(type));
         }
@@ -170,6 +181,11 @@ public class WalkerRules {
             super(source.predicates);
         }
 
+        public RuleBuilderType<F> text(Function<ObjectContext<F>, String> formatter) {
+            this.formatter = ctx -> ctx.text(formatter.apply(ctx));
+            return this;
+        }
+
         public RuleBuilderMore match(Class<?> type) {
             return match(IClass.typeinfo(type));
         }
@@ -192,15 +208,22 @@ public class WalkerRules {
             super(source.predicates);
         }
 
+        public RuleBuilderMore text(Function<ObjectContext<Object>, String> formatter) {
+            this.formatter = ctx -> ctx.text(formatter.apply(ctx));
+            return this;
+        }
+
         public RuleBuilderMore match(Class<?> type) {
             return match(IClass.typeinfo(type));
         }
 
         public RuleBuilderMore match(IClass<?> type) {
+            predicates.add(ctx -> ctx.isSubclass(type));
             return this;
         }
 
-        public RuleBuilderMore match(Predicate<ObjectContext<?>> predicate) {
+        public RuleBuilderMore match(RulePredicate<?> predicate) {
+            predicates.add(predicate);
             return this;
         }
 

+ 1 - 0
assira/src/test/java/net/ranides/assira/TestFiles.java

@@ -86,6 +86,7 @@ public final class TestFiles {
     public static final File WALK_FORMAT = new File(WALK_DIR,"format.txt");
     public static final File WALK_FORMAT_RULES = new File(WALK_DIR,"formatRules.txt");
     public static final File WALK_FORMAT_RECURSIVE = new File(WALK_DIR,"formatRecursive.txt");
+    public static final File WALK_ARRAYS = new File(WALK_DIR,"arrays.txt");
 
 
 }

+ 12 - 0
assira/src/test/java/net/ranides/assira/reflection/ResolvePatternTest.java

@@ -18,6 +18,8 @@ import net.ranides.assira.generic.TypeToken;
 import net.ranides.assira.generic.Wrapper;
 import net.ranides.assira.reflection.mockup.ForResolver.*;
 import net.ranides.assira.reflection.util.ResolveUtils;
+
+import org.junit.Assume;
 import org.junit.Test;
 import static net.ranides.assira.junit.NewAssert.*;
 import net.ranides.assira.reflection.mockup.ForIClass;
@@ -240,6 +242,16 @@ public class ResolvePatternTest {
             ResolveUtils.replace(a, "2", 'S');
         });
     }
+
+    @Test
+    public void testArray() {
+        try {
+            A context = new A();
+            assertEquals(3, ResolveUtils.get(context, "array.length"));
+        } catch (ResolveException e) {
+            Assume.assumeTrue("Sorry, we can't resolve array.length", false);
+        }
+    }
     
     @Test
     public void testType() {

+ 26 - 0
assira/src/test/java/net/ranides/assira/reflection/walker/ObjectWalkerTest.java

@@ -11,6 +11,7 @@ import net.ranides.assira.reflection.*;
 import net.ranides.assira.reflection.walker.ObjectVisitor.PrintVisitor;
 import net.ranides.assira.reflection.walker.WalkerContexts.ObjectContext;
 import net.ranides.assira.text.Charsets;
+import net.ranides.assira.text.FormatHex;
 import net.ranides.assira.text.IOStrings;
 
 import java.io.IOException;
@@ -60,6 +61,13 @@ public class ObjectWalkerTest {
         .point(77, new Point(7,7,7))
         .build();
 
+    Record record1 = Record.builder()
+        .uid(556)
+        .binary(FormatHex.parse("32 56 a0 f3 47 8a"))
+        .sequence("hello world".toCharArray())
+        .lines("one two three four five six".split(" "))
+        .build();
+
     @Test
     public void basic() throws IOException {
         StringWriter writer = new StringWriter();
@@ -215,6 +223,24 @@ public class ObjectWalkerTest {
         assertEquals(IOStrings.read(TestFiles.WALK_FORMAT_RECURSIVE, Charsets.UTF8), writer.toString());
     }
 
+    @Test
+    public void arrays() throws IOException {
+        StringWriter writer = new StringWriter();
+
+        List<Object> list = Arrays.asList(record1, foo1);
+        ObjectWalker.walk(list, "record", WalkerRules.DEFAULT.visitor(writer));
+
+        assertEquals(IOStrings.read(TestFiles.WALK_ARRAYS, Charsets.UTF8), writer.toString());
+    }
+
+    @Data
+    @Builder
+    public static class Record {
+        private final int uid;
+        private final byte[] binary;
+        private final char[] sequence;
+        private final String[] lines;
+    }
 
 
     @Data

+ 46 - 0
assira/src/test/resources/reflective/walker/arrays.txt

@@ -0,0 +1,46 @@
+   1.record                                             List<E>.size = 2
+   2.record/[0]                                         Record = ObjectWalkerTest.Record(uid=556, binary=[50, 86, -96, -13, 71, -118], sequence=[h, e, l, l, o,  , w, o, r, l, d], lines=[one, two, three, four, five, six])
+   3.record/[0]/binary                                  byte[] = '32 56 A0 F3 47 8A'
+   4.record/[0]/lines                                   String[].size = 6
+   5.record/[0]/lines/[0]                               String = 'one'
+   6.record/[0]/lines/[1]                               String = 'two'
+   7.record/[0]/lines/[2]                               String = 'three'
+   8.record/[0]/lines/[3]                               String = 'four'
+   9.record/[0]/lines/[4]                               String = 'five'
+  10.record/[0]/lines/[5]                               String = 'six'
+  11.record/[0]/sequence                                char[] = 'hello world'
+  12.record/[0]/uid                                     Integer = 556
+  13.record/[1]                                         Foo = ObjectWalkerTest.Foo(bar=ObjectWalkerTest.Bar(uid=456, hex=0xFAD), names=[John, Jenny, Adam], empty=null, points={17=ObjectWalkerTest.Point(x=21, y=6, z=4), 18=ObjectWalkerTest.Point(x=21, y=6, z=4), 3=ObjectWalkerTest.Point(x=9, y=19, z=11), 8=ObjectWalkerTest.Point(x=9, y=32, z=2), 21=ObjectWalkerTest.Point(x=9, y=7, z=5), 88=ObjectWalkerTest.Point(x=21, y=6, z=4)})
+  14.record/[1]/bar                                     Bar = ObjectWalkerTest.Bar(uid=456, hex=0xFAD)
+  15.record/[1]/bar/hex                                 String = '0xFAD'
+  16.record/[1]/bar/uid                                 String = '456'
+  17.record/[1]/empty                                   Bar = <null>
+  18.record/[1]/names                                   List<String>.size = 3
+  19.record/[1]/names/[0]                               String = 'John'
+  20.record/[1]/names/[1]                               String = 'Jenny'
+  21.record/[1]/names/[2]                               String = 'Adam'
+  22.record/[1]/points                                  Map<Integer,Point>.size = 6
+  23.record/[1]/points/[17]                             Point = ObjectWalkerTest.Point(x=21, y=6, z=4)
+  24.record/[1]/points/[17]/x                           Integer = 21
+  25.record/[1]/points/[17]/y                           Integer = 6
+  26.record/[1]/points/[17]/z                           Integer = 4
+  27.record/[1]/points/[18]                             Point = ObjectWalkerTest.Point(x=21, y=6, z=4)
+  28.record/[1]/points/[18]/x                           Integer = 21
+  29.record/[1]/points/[18]/y                           Integer = 6
+  30.record/[1]/points/[18]/z                           Integer = 4
+  31.record/[1]/points/[21]                             Point = ObjectWalkerTest.Point(x=9, y=7, z=5)
+  32.record/[1]/points/[21]/x                           Integer = 9
+  33.record/[1]/points/[21]/y                           Integer = 7
+  34.record/[1]/points/[21]/z                           Integer = 5
+  35.record/[1]/points/[3]                              Point = ObjectWalkerTest.Point(x=9, y=19, z=11)
+  36.record/[1]/points/[3]/x                            Integer = 9
+  37.record/[1]/points/[3]/y                            Integer = 19
+  38.record/[1]/points/[3]/z                            Integer = 11
+  39.record/[1]/points/[8]                              Point = ObjectWalkerTest.Point(x=9, y=32, z=2)
+  40.record/[1]/points/[8]/x                            Integer = 9
+  41.record/[1]/points/[8]/y                            Integer = 32
+  42.record/[1]/points/[8]/z                            Integer = 2
+  43.record/[1]/points/[88]                             Point = ObjectWalkerTest.Point(x=21, y=6, z=4)
+  44.record/[1]/points/[88]/x                           Integer = 21
+  45.record/[1]/points/[88]/y                           Integer = 6
+  46.record/[1]/points/[88]/z                           Integer = 4

+ 3 - 3
assira/src/test/resources/reflective/walker/format.txt

@@ -36,12 +36,12 @@ foo1/points/[88]/z = 4
    2.foo2/bar                                           Bar = ObjectWalkerTest.Bar(uid=456, hex=0xFAD)
    3.foo2/bar/hex                                       String = '0xFAD'
    4.foo2/bar/uid                                       String = '456'
-   5.foo2/empty                                         = <null>
-   6.foo2/names                                         List<String>
+   5.foo2/empty                                         Bar = <null>
+   6.foo2/names                                         List<String>.size = 3
    7.foo2/names/[0]                                     String = 'John'
    8.foo2/names/[1]                                     String = 'Jenny'
    9.foo2/names/[2]                                     String = 'Adam'
-  10.foo2/points                                        UnmodifiableMap = {17=ObjectWalkerTest.Point(x=21, y=6, z=4), 18=ObjectWalkerTest.Point(x=21, y=6, z=4), 3=ObjectWalkerTest.Point(x=9, y=19, z=11), 8=ObjectWalkerTest.Point(x=9, y=32, z=2), 21=ObjectWalkerTest.Point(x=9, y=7, z=5), 88=ObjectWalkerTest.Point(x=21, y=6, z=4)}
+  10.foo2/points                                        Map<Integer,Point>.size = 6
   11.foo2/points/[17]                                   Point = ObjectWalkerTest.Point(x=21, y=6, z=4)
   12.foo2/points/[17]/x                                 Integer = 21
   13.foo2/points/[17]/y                                 Integer = 6

+ 3 - 3
assira/src/test/resources/reflective/walker/formatRecursive.txt

@@ -8,7 +8,7 @@
 #   ObjectContext:4    8.list/[0]/names/[0]                                 String = 'John'
 #   ObjectContext:4    9.list/[0]/names/[1]                                 String = 'Jenny'
 #   ObjectContext:4   10.list/[0]/names/[2]                                 String = 'Adam'
-#      MapContext:3   11.list/[0]/points                                    UnmodifiableMap = {17=ObjectWalkerTest.Point(x=21, y=6, z=4), 18=ObjectWalkerTest.Point(x=21, y=6, z=4), 88=ObjectWalkerTest.Point(x=21, y=6, z=4)}
+#      MapContext:3   11.list/[0]/points                                    Map<Integer,Point>
 #   ObjectContext:4   12.list/[0]/points/[17]                               Point = ObjectWalkerTest.Point(x=21, y=6, z=4)
 #   ObjectContext:5   13.list/[0]/points/[17]/x                             Integer = 21
 #   ObjectContext:5   14.list/[0]/points/[17]/y                             Integer = 6
@@ -28,7 +28,7 @@
 #   ObjectContext:3   28.list/[1]/empty                                     <null>
 #     ListContext:3   29.list/[1]/names                                     List<String>
 #   ObjectContext:4   30.list/[1]/names/[0]                                 String = 'Tom'
-#      MapContext:3   31.list/[1]/points                                    SingletonMap = {33=ObjectWalkerTest.Point(x=3, y=3, z=3)}
+#      MapContext:3   31.list/[1]/points                                    Map<Integer,Point>
 #   ObjectContext:4   32.list/[1]/points/[33]                               Point = ObjectWalkerTest.Point(x=3, y=3, z=3)
 #   ObjectContext:5   33.list/[1]/points/[33]/x                             Integer = 3
 #   ObjectContext:5   34.list/[1]/points/[33]/y                             Integer = 3
@@ -41,7 +41,7 @@
 #   ObjectContext:3   41.list/[3]/empty                                     <null>
 #     ListContext:3   42.list/[3]/names                                     List<String>
 #   ObjectContext:4   43.list/[3]/names/[0]                                 String = 'Ann'
-#      MapContext:3   44.list/[3]/points                                    SingletonMap = {77=ObjectWalkerTest.Point(x=7, y=7, z=7)}
+#      MapContext:3   44.list/[3]/points                                    Map<Integer,Point>
 #   ObjectContext:4   45.list/[3]/points/[77]                               Point = ObjectWalkerTest.Point(x=7, y=7, z=7)
 #   ObjectContext:5   46.list/[3]/points/[77]/x                             Integer = 7
 #   ObjectContext:5   47.list/[3]/points/[77]/y                             Integer = 7