Pārlūkot izejas kodu

new: DynamicInvoker: null & identity
new: Lookup: #inc #dec

Mariusz Sieroń 6 gadi atpakaļ
vecāks
revīzija
d619aff6c3

+ 24 - 1
assira/src/main/java/net/ranides/assira/collection/lookups/ALookup.java

@@ -207,7 +207,30 @@ public abstract class ALookup<K> implements Lookup<K> {
     public Integer remove(Object key) {
         return containsKey(key) ? removeInt(key) : null;
     }
-    
+
+    public int inc(K key) {
+        return modify(key, +1);
+    }
+
+    public int dec(K key) {
+        return modify(key, -1);
+    }
+
+    private int modify(K key, int delta) {
+        int v;
+        if(containsKey(key)) {
+            v = getInt(key) + delta;
+        } else {
+            v = defaultReturnValue() + delta;
+        }
+        if(v == defaultReturnValue()) {
+            remove(key);
+        } else {
+            put(key, v);
+        }
+        return v;
+    }
+
     /**
      * This class provides a basic but complete type-specific entry class for
      * all those maps implementations that do not have entries on their own

+ 4 - 0
assira/src/main/java/net/ranides/assira/collection/lookups/Lookup.java

@@ -43,6 +43,10 @@ public interface Lookup<K> extends Map<K, Integer>, Serializable {
 
     int removeInt(Object key);
 
+    int inc(K key);
+
+    int dec(K key);
+
     @Override
     IntCollection values();
     

+ 35 - 3
assira/src/main/java/net/ranides/assira/generic/DynamicInvoker.java

@@ -22,8 +22,14 @@ public class DynamicInvoker {
 
     private final List<RFunction> list;
 
+    private final boolean passNull;
+
+    private final boolean passIdent;
+
     private DynamicInvoker(Builder src) {
         this.list = src.list;
+        this.passNull = src.passNull;
+        this.passIdent = src.passIdent;
     }
 
     public static Builder builder() {
@@ -32,12 +38,15 @@ public class DynamicInvoker {
 
     public Object call(Object value) {
         Function<Object, Object> func = match(value)
-            .orElseThrow(() -> new IllegalArgumentException("No method found for: " + value.getClass()));
+            .orElseThrow(() -> new IllegalArgumentException("No method found for: " + IClass.typefor(value)));
         return func.apply(value);
     }
 
     @SuppressWarnings("unchecked")
     public Optional<Function<Object, Object>> match(Object value) {
+        if(value == null && passNull) {
+            return Optional.of(v -> v);
+        }
         for (RFunction r : list) {
             if (r.arg.isInstance(value)) {
                 return Optional.of(r.func);
@@ -48,13 +57,13 @@ public class DynamicInvoker {
 
     public <T,R> R call(Class<R> returns, T value) {
         Function<T, R> func = match(returns, value)
-                .orElseThrow(() -> new IllegalArgumentException("No method found for: " + value.getClass() + "=>" + ClassUtils.box(returns)));
+                .orElseThrow(() -> new IllegalArgumentException("No method found for: " + IClass.typefor(value) + " =>" + ClassUtils.box(returns)));
         return func.apply(value);
     }
 
     public <T,R> R call(IClass<R> returns, T value) {
         Function<T, R> func = match(returns, value)
-                .orElseThrow(() -> new IllegalArgumentException("No method found for: " + value.getClass() + "=>" + ClassUtils.box(returns)));
+                .orElseThrow(() -> new IllegalArgumentException("No method found for: " + IClass.typefor(value) + " => " + ClassUtils.box(returns)));
         return func.apply(value);
     }
 
@@ -64,6 +73,12 @@ public class DynamicInvoker {
 
     @SuppressWarnings("unchecked")
     public <T, R> Optional<Function<T,R>> match(IClass<R> returns, T value) {
+        if(value == null && passNull) {
+            return Optional.of(v -> null);
+        }
+        if(value!=null && returns.isInstance(value) && passIdent) {
+            return Optional.of(v -> returns.cast(v));
+        }
         for (RFunction r : list) {
             if (r.arg.isInstance(value)) {
                 if(returns.isSuper(r.ret)) {
@@ -72,6 +87,9 @@ public class DynamicInvoker {
 
             }
         }
+        if(returns.raw().isInstance(value) && passIdent) {
+            return Optional.of(v -> returns.cast(v));
+        }
         return Optional.empty();
     }
 
@@ -81,6 +99,20 @@ public class DynamicInvoker {
 
         private final List<RFunction> list = new ArrayList<>();
 
+        private boolean passNull = true;
+
+        private boolean passIdent = true;
+
+        public Builder passIdent(boolean passIdent) {
+            this.passIdent = passIdent;
+            return this;
+        }
+
+        public Builder passNull(boolean passNull) {
+            this.passNull = passNull;
+            return this;
+        }
+
         public <T,R> Builder append(Class<T> argument, Class<R> returns, Function<T, R> function) {
             return append0(argument, IClass.typeinfo(returns), function);
         }

+ 26 - 3
assira/src/test/java/net/ranides/assira/collection/lookups/RBTreeLookupTest.java

@@ -6,9 +6,8 @@
  */
 package net.ranides.assira.collection.lookups;
 
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
+import java.util.*;
+
 import net.ranides.assira.collection.mockup.TMaps;
 import net.ranides.assira.collection.mockup.TPoint;
 import net.ranides.assira.ContractTesters;
@@ -73,5 +72,29 @@ public class RBTreeLookupTest {
             new RBTreeLookup<>($map.range(60).keys(), $map.range(40).valuesInt(), cmp1)
         );
     }
+
+    @Test
+    public void counter() {
+        Lookup<String> map = new RBTreeLookup<>();
+        map.inc("a");
+        map.inc("a");
+        map.inc("a");
+        map.inc("b");
+        map.inc("b");
+        map.inc("c");
+
+        assertEquals(Arrays.asList("a", "b", "c"), new ArrayList<>(map.keySet()));
+
+        map.dec("a");
+        map.dec("b");
+        map.dec("c");
+
+        assertEquals(Arrays.asList("a", "b"), new ArrayList<>(map.keySet()));
+
+        map.dec("a");
+        map.dec("b");
+
+        assertEquals(Arrays.asList("a"), new ArrayList<>(map.keySet()));
+    }
     
 }

+ 29 - 2
assira/src/test/java/net/ranides/assira/generic/DynamicInvokerTest.java

@@ -2,6 +2,9 @@ package net.ranides.assira.generic;
 
 import lombok.RequiredArgsConstructor;
 import net.ranides.assira.annotations.Meta;
+import net.ranides.assira.junit.NewAssert;
+import net.ranides.assira.reflection.IClass;
+import org.junit.Assert;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -9,8 +12,7 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.*;
 
 public class DynamicInvokerTest {
 
@@ -64,6 +66,31 @@ public class DynamicInvokerTest {
         assertEquals("7+7=14", di.call(String.class, 7));
     }
 
+    @Test
+    public void specials() {
+
+        DynamicInvoker di1 = DynamicInvoker.builder().appendDelegate(new InvokeHelper(0)).passNull(false).passIdent(false).build();
+        DynamicInvoker di2 = DynamicInvoker.builder().appendDelegate(new InvokeHelper(0)).passNull(true).passIdent(false).build();
+        DynamicInvoker di3 = DynamicInvoker.builder().appendDelegate(new InvokeHelper(0)).passNull(false).passIdent(true).build();
+
+        NewAssert.assertThrows(IllegalArgumentException.class, () -> {
+            di1.call(Long.class, null);
+        });
+        NewAssert.assertThrows(IllegalArgumentException.class, () -> {
+            di1.call(Long.class, 45L);
+        });
+
+        assertNull(di2.call(Long.class, null));
+        NewAssert.assertThrows(IllegalArgumentException.class, () -> {
+            di2.call(Long.class, 45L);
+        });
+
+        NewAssert.assertThrows(IllegalArgumentException.class, () -> {
+            di3.call(Long.class, null);
+        });
+        assertEquals((Long)45L, di3.call(Long.class, 45L));
+    }
+
     @Test
     public void generic() {