Repository: brooklyn-server Updated Branches: refs/heads/master a3d0ea06e -> d34266387
improve CollectionMerger to take a configurable list merge function Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/e8427f7f Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/e8427f7f Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/e8427f7f Branch: refs/heads/master Commit: e8427f7fa938f76eb75f0dc9d478d27a4f575ccd Parents: e27cc7b Author: Alex Heneveld <alex.henev...@cloudsoftcorp.com> Authored: Tue Sep 25 15:20:30 2018 +0100 Committer: Alex Heneveld <alex.henev...@cloudsoftcorp.com> Committed: Tue Sep 25 15:20:30 2018 +0100 ---------------------------------------------------------------------- .../util/collections/CollectionMerger.java | 64 +++++++++----------- .../util/collections/CollectionMergerTest.java | 16 +++++ 2 files changed, 43 insertions(+), 37 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e8427f7f/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionMerger.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionMerger.java b/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionMerger.java index 337e4cc..8707fb9 100644 --- a/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionMerger.java +++ b/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionMerger.java @@ -23,10 +23,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Collection; import java.util.Date; -import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.BiFunction; import org.apache.brooklyn.util.guava.Maybe; @@ -43,8 +44,8 @@ public class CollectionMerger { public static class Builder { protected int depth = Integer.MAX_VALUE; protected boolean mergeNestedMaps = true; - protected boolean mergeNestedLists = false; protected boolean preferSecondOnConflict = false; + protected BiFunction<Collection<?>, Collection<?>, Collection<?>> mergeNestedLists = null; /** Sets effectively infinite {@link #depth(int)} (the default) */ public Builder deep(boolean val) { @@ -71,10 +72,25 @@ s * } /** By default lists will not be merged, and either the first or second will be kept * depending on {@link #preferSecondOnConflict(boolean)}. Set this to true to cause - * lists in the second merge argument to be appended to lists in the first. + * lists in the second merge argument to be appended to lists in the first, or false for the default behaviour. + * For more complex behaviours use {@link #mergeNestedLists(BiFunction)}. */ public Builder mergeNestedLists(boolean val) { - this.mergeNestedLists = val; + if (val) { + mergeNestedLists((l1,l2) -> { + Collection<Object> result = l1 instanceof Set ? MutableSet.of() : MutableList.of(); + result.addAll(l1); + result.addAll(l2); + return result; + }); + } else { + mergeNestedLists(null); + } + return this; + } + /** Defines a function to use to determine the result when lists or sets need merging. */ + public Builder mergeNestedLists(BiFunction<Collection<?>, Collection<?>, Collection<?>> listMergeFunction) { + this.mergeNestedLists = listMergeFunction; return this; } /** defaults to false, so if there is an unmergeable conflict, e.g. two strings, the first will be kept */ @@ -92,7 +108,7 @@ s * } protected final int depth; - protected final boolean mergeNestedLists; + protected final BiFunction<Collection<?>, Collection<?>, Collection<?>> mergeNestedLists; protected final boolean preferSecondOnConflict; protected CollectionMerger(Builder builder) { @@ -142,15 +158,14 @@ s * } } if (val1.get() instanceof Iterable) { - if (!mergeNestedLists) { - return val1.get(); - } - Iterable<?> iter1 = (Iterable<?>) val1.get(); - if (val2.get() instanceof Iterable) { - return mergeIterablesImpl(iter1, (Iterable<?>) val2.get(), depthRemaining, visited); - } else { + if (mergeNestedLists == null) { + return conflictResult; + } else if (!(val2.get() instanceof Iterable)) { // incompatible types; not merging return conflictResult; + } else { + Collection<?> iter1 = val1.get() instanceof Collection ? (Collection<?>) val1.get() : MutableList.copyOf((Iterable<?>)val1.get()); + return mergeNestedLists.apply(iter1, MutableList.copyOf((Iterable<?>)val2.get())); } } return conflictResult; @@ -169,31 +184,6 @@ s * return result; } - private Iterable<?> mergeIterablesImpl(Iterable<?> val1, Iterable<?> val2, int depthRemaining, Visited visited) { - if (depthRemaining < 1) { - return val1; - } - if (val1 instanceof Set) { - return mergeSetsImpl((Set<?>)val1, MutableSet.copyOf(val2), depthRemaining, visited); - } else { - return mergeListsImpl(MutableList.copyOf(val1), val2, depthRemaining, visited); - } - } - - private Set<?> mergeSetsImpl(Set<?> val1, Set<?> val2, int depthRemaining, Visited visited) { - return MutableSet.builder() - .addAll(val1) - .addAll(val2) - .build(); - } - - private List<?> mergeListsImpl(List<?> val1, Iterable<?> val2, int depthRemaining, Visited visited) { - return MutableList.builder() - .addAll(val1) - .addAll(val2) - .build(); - } - /** * For avoiding infinite loops, we need to know which objects we have already visited. * If we come across that object again, then want to return the same result (rather than http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e8427f7f/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionMergerTest.java ---------------------------------------------------------------------- diff --git a/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionMergerTest.java b/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionMergerTest.java index 2b0bf76..5800560 100644 --- a/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionMergerTest.java +++ b/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionMergerTest.java @@ -22,6 +22,8 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotEquals; import java.io.StringReader; +import java.util.Collections; +import java.util.List; import java.util.Map; import org.apache.brooklyn.test.Asserts; @@ -428,6 +430,20 @@ public class CollectionMergerTest { assertEquals(result2, ImmutableMap.of("key1", "val1b", "key2", "val2", "key3", "val3")); } + @Test + public void testMergeListsWithCusomFunction() { + Map<?,?> val1 = ImmutableMap.of("key", ImmutableList.of("a", "c")); + Map<?,?> val2 = ImmutableMap.of("key", ImmutableList.of("b")); + + Map<?, ?> result = CollectionMerger.builder().mergeNestedLists( (l1,l2) -> { + List<Object> r = MutableList.<Object>copyOf(l1).appendAll(l2); + Collections.sort(r, (o1,o2) -> o1.toString().compareTo(o2.toString())); + return r; + }).build().merge(val1, val2); + + assertEquals(result, ImmutableMap.of("key", ImmutableList.of("a", "b", "c"))); + } + protected Iterable<?> parseYaml(String yaml) { return new Yaml().loadAll(new StringReader(yaml)); }