Procházet zdrojové kódy

fix: fluent map in BeanModel contains 1-argument method calls

Ranides Atterwim před 2 roky
rodič
revize
b940ea50f1

+ 29 - 13
assira.core/src/main/java/net/ranides/assira/reflection/impl/bean/RBeanModel.java

@@ -36,7 +36,8 @@ public class RBeanModel implements BeanModel, Serializable {
 
     private final IClass<?> type;
     private final Map<String, BeanMethod> methods;
-    private final Map<String, BeanMethod> fluent;
+    private final Map<String, BeanMethod> fluentGet;
+    private final Map<String, BeanMethod> fluentSet;
     private final Map<String, BeanProperty> properties;
     private final Set<String> keys;
 
@@ -46,7 +47,8 @@ public class RBeanModel implements BeanModel, Serializable {
         Map<String, RBeanPropertyBuilder> pinfo = new LazyMap<>(new OpenMap<>(), RBeanPropertyBuilder::new);
         MultiMap<String, IMethod> minfo = new OpenMultiMap<>();
 
-        fluent = new OpenMap<>();
+        fluentGet = new OpenMap<>();
+        fluentSet = new OpenMap<>();
 
         for(IMethod m : type.methods()) {
             if(m.parent().equals(IClass.OBJECT) && !OBJECT_METHODS.contains(m.name())) {
@@ -68,7 +70,11 @@ public class RBeanModel implements BeanModel, Serializable {
             else {
                 minfo.put(m.name(), m);
                 if(m.arguments().count() == 0 && !m.parent().equals(IClass.OBJECT)) {
-                    fluent.put(m.name(), new RBeanMethod(m));
+                    fluentGet.put(m.name(), new RBeanMethod(m));
+                }
+
+                if(m.arguments().count() == 1 && !m.parent().equals(IClass.OBJECT) && (m.returns().isVoid() || m.returns().isSuper(m.parent())) ) {
+                    fluentSet.put(m.name(), new RBeanMethod(m));
                 }
             }
         }
@@ -79,9 +85,9 @@ public class RBeanModel implements BeanModel, Serializable {
         properties = new OpenMap<>();
         for(Entry<String, RBeanPropertyBuilder> item : pinfo.entrySet()) {
             properties.put(item.getKey(), FBeanModel.resolve(item.getValue()));
-            fluent.remove(item.getKey());
+            fluentGet.remove(item.getKey());
         }
-        this.keys = new FlatSet<String>().join(properties.keySet()).join(fluent.keySet());
+        this.keys = new FlatSet<String>().join(properties.keySet()).join(fluentGet.keySet()).join(fluentSet.keySet());
     }
     
     @Override
@@ -284,13 +290,13 @@ public class RBeanModel implements BeanModel, Serializable {
 
         @Override
         public boolean containsKey(Object key) {
-            return properties.containsKey(key) || fluent.containsKey(key);
+            return properties.containsKey(key) || fluentGet.containsKey(key) || fluentSet.containsKey(key);
         }
 
         @Override
         public boolean containsValue(Object value) {
-            return properties.values().stream().map(p -> p.get(that)).anyMatch(v -> Objects.equals(v, value)) &&
-                    fluent.values().stream().map(p -> p.invoke(that)).anyMatch(v -> Objects.equals(v, value));
+            return properties.values().stream().map(p -> p.get(that)).anyMatch(v -> Objects.equals(v, value)) ||
+                 fluentGet.values().stream().map(p -> p.invoke(that)).anyMatch(v -> Objects.equals(v, value));
         }
 
         @Override
@@ -299,7 +305,12 @@ public class RBeanModel implements BeanModel, Serializable {
             if(p != null) {
                 return p.replace(that, value);
             }
-            BeanMethod m = fluent.get(key);
+            BeanMethod s = fluentSet.get(key);
+            if(s != null) {
+                s.invoke(that, value);
+                return null;
+            }
+            BeanMethod m = fluentGet.get(key);
             if(m != null) {
                 throw FBeanModel.newUnsupportedOperationException("Computed fluent property " + key + " is read-only");
             }
@@ -315,10 +326,14 @@ public class RBeanModel implements BeanModel, Serializable {
             if(p != null) {
                 return p.get(that);
             }
-            BeanMethod m = fluent.get(key);
+            BeanMethod m = fluentGet.get(key);
             if(m != null) {
                 return m.invoke(that);
             }
+            BeanMethod s = fluentSet.get(key);
+            if(s != null) {
+                throw FBeanModel.newUnsupportedOperationException("Computed fluent property " + key + " is write-only");
+            }
             return null;
         }
 
@@ -333,13 +348,14 @@ public class RBeanModel implements BeanModel, Serializable {
         public Iterator<Entry<String, Object>> iterator() {
             return IteratorUtils.concat(
                     IteratorUtils.map(properties.values().iterator(), p -> new PEntry(p, that)),
-                    IteratorUtils.map(fluent.values().iterator(), p1 -> new MEntry(p1, that))
+                    IteratorUtils.map(fluentGet.values().iterator(), p -> new MEntry(p, that)),
+                    IteratorUtils.map(fluentSet.values().iterator(), p -> new MEntry(p, that))
             );
         }
 
         @Override
         public int size() {
-            return properties.size() + fluent.size();
+            return properties.size() + fluentGet.size();
         }
 
         @Override
@@ -358,7 +374,7 @@ public class RBeanModel implements BeanModel, Serializable {
             if(p != null) {
                 return Objects.equals(entry.getValue(), p.get(that));
             }
-            BeanMethod m = fluent.get(key);
+            BeanMethod m = fluentGet.get(key);
             if(m != null) {
                 return Objects.equals(entry.getValue(), m.invoke(that));
             }

+ 13 - 5
assira.core/src/test/java/net/ranides/assira/reflection/impl/bean/RBeanModelTest.java

@@ -296,7 +296,7 @@ public class RBeanModelTest {
 
     @Test
     public void testGeneric() {
-        Point<Integer> point = new Point<>(5, 6);
+        Point<Integer> point = new Point<>(1, 5, 6);
         BeanModel model = BeanModel.typefor(point);
 
         assertTrue(model.properties().get("x").isReadable());
@@ -305,11 +305,11 @@ public class RBeanModelTest {
 
     @Test
     public void testFluent() {
-        Point<Integer> point = new Point<>(5, 6);
+        Point<Integer> point = new Point<>(1, 5, 6);
         BeanModel model = BeanModel.typefor(point);
         Map<String, Object> fluent = model.fluent(point);
 
-        assertEquals(Arrays.asList("length", "x", "y"), new ArrayList<>(new TreeSet<>(fluent.keySet())));
+        assertEquals(Arrays.asList("length", "updateScale", "x", "y"), new ArrayList<>(new TreeSet<>(fluent.keySet())));
         assertEquals(5, fluent.get("x"));
         assertEquals(6, fluent.get("y"));
         assertEquals(8, fluent.get("length"));
@@ -317,6 +317,8 @@ public class RBeanModelTest {
         fluent.put("x", 3);
         fluent.put("y", 4);
         assertEquals(5, fluent.get("length"));
+        fluent.put("updateScale", 4);
+        assertEquals(20, fluent.get("length"));
 
         assertThrows(UnsupportedOperationException.class, IReflectiveException.class, () -> {
             fluent.put("length", 4);
@@ -338,10 +340,12 @@ public class RBeanModelTest {
     }
 
     public static class Point<N extends Number> {
+        private N scale;
         private N x;
         private N y;
 
-        public Point(N x, N y) {
+        public Point(N scale, N x, N y) {
+            this.scale = scale;
             this.x = x;
             this.y = y;
         }
@@ -363,7 +367,11 @@ public class RBeanModelTest {
         }
 
         public int length() {
-            return (int)Math.round(Math.sqrt(x.intValue() * x.intValue() + y.intValue() * y.intValue()));
+            return scale.intValue() * (int)Math.round(Math.sqrt(x.intValue() * x.intValue() + y.intValue() * y.intValue()));
+        }
+
+        public void updateScale(N scale) {
+            this.scale = scale;
         }
     }
 }