diff --git a/ax-adt/src/main/java/com/g2forge/alexandria/adt/ReduceStrategy.java b/ax-adt/src/main/java/com/g2forge/alexandria/adt/ReduceStrategy.java new file mode 100644 index 00000000..a2c00e4a --- /dev/null +++ b/ax-adt/src/main/java/com/g2forge/alexandria/adt/ReduceStrategy.java @@ -0,0 +1,66 @@ +package com.g2forge.alexandria.adt; + +import java.util.Objects; + +import com.g2forge.alexandria.java.core.error.UnreachableCodeError; +import com.g2forge.alexandria.java.function.IConsumer1; +import com.g2forge.alexandria.java.function.IFunction1; +import com.g2forge.alexandria.java.function.IFunction2; + +/** + * Strategies for reducing fields of POJOs. + */ +public enum ReduceStrategy { + NullIsNone { + @Override + public void reduce(T t0, T t1, IFunction1 getter, IConsumer1 setter, IFunction2 merge) { + final V v0 = getter.apply(t0); + final V v1 = getter.apply(t1); + switch (computeIndex(v0, v1)) { + case 0: + break; + case 1: + setter.accept(v0); + break; + case 2: + setter.accept(v1); + break; + case 3: + if (merge != null) { + final V merged = merge.apply(v0, v1); + setter.accept(merged); + break; + } else if (Objects.equals(v0, v1)) { + setter.accept(v0); + break; + } else throw new IllegalArgumentException(String.format("Cannot merge non-null values (%1$s and %2$s), as no merging function as supplied", v0, v1)); + default: + throw new UnreachableCodeError(); + } + } + }, + NullIsNoneSkip0 { + @Override + public void reduce(T t0, T t1, IFunction1 getter, IConsumer1 setter, IFunction2 merge) { + final V v0 = getter.apply(t0); + final V v1 = getter.apply(t1); + switch (computeIndex(v0, v1)) { + case 0: + case 1: + break; + case 2: + case 3: + setter.accept(v1); + break; + default: + throw new UnreachableCodeError(); + } + } + }; + + protected int computeIndex(final V v0, final V v1) { + return ((v1 != null) ? 2 : 0) | ((v0 != null) ? 1 : 0); + } + + public abstract void reduce(T t0, T t1, IFunction1 getter, IConsumer1 setter, IFunction2 merge); +} \ No newline at end of file diff --git a/ax-adt/src/test/java/com/g2forge/alexandria/adt/TestReduceStrategy.java b/ax-adt/src/test/java/com/g2forge/alexandria/adt/TestReduceStrategy.java new file mode 100644 index 00000000..e45e347e --- /dev/null +++ b/ax-adt/src/test/java/com/g2forge/alexandria/adt/TestReduceStrategy.java @@ -0,0 +1,47 @@ +package com.g2forge.alexandria.adt; + +import org.junit.Test; + +import com.g2forge.alexandria.test.HAssert; + +import lombok.Builder; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +public class TestReduceStrategy { + @Data + @Builder(toBuilder = true) + @RequiredArgsConstructor + public static class Record { + protected final String field; + } + + @Test + public void none() { + final Record.RecordBuilder builder = Record.builder(); + ReduceStrategy.NullIsNone.reduce(new Record(null), new Record(null), Record::getField, builder::field, null); + HAssert.assertNull(builder.build().getField()); + } + + @Test + public void a() { + final Record.RecordBuilder builder = Record.builder(); + ReduceStrategy.NullIsNone.reduce(new Record("A"), new Record(null), Record::getField, builder::field, null); + HAssert.assertEquals("A", builder.build().getField()); + } + + @Test + public void b() { + final Record.RecordBuilder builder = Record.builder(); + ReduceStrategy.NullIsNone.reduce(new Record(null), new Record("B"), Record::getField, builder::field, null); + HAssert.assertEquals("B", builder.build().getField()); + } + + @Test + public void merge() { + final Record.RecordBuilder builder = Record.builder(); + final Record a = new Record("A"); + final Record b = new Record("B"); + HAssert.assertThrows(IllegalArgumentException.class, () -> ReduceStrategy.NullIsNone.reduce(a, b, Record::getField, builder::field, null)); + } +}