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));
     }

Reply via email to