Parcourir la source

[3339] data::merge now handles map elements

data::merge function is intended to merge to map elements together.
However, if those maps contain elements which are themselves maps they
are not merged.  The function uses recursion to merge map sub-elements.
Thomas Markwalder il y a 11 ans
Parent
commit
ed75a8ddb7
2 fichiers modifiés avec 61 ajouts et 17 suppressions
  1. 20 3
      src/lib/cc/data.cc
  2. 41 14
      src/lib/cc/tests/data_unittests.cc

+ 20 - 3
src/lib/cc/data.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-2014 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -964,9 +964,26 @@ merge(ElementPtr element, ConstElementPtr other) {
 
 
     const std::map<std::string, ConstElementPtr>& m = other->mapValue();
     const std::map<std::string, ConstElementPtr>& m = other->mapValue();
     for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
     for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
-         it != m.end() ; ++it) {
+        it != m.end() ; ++it) {
         if ((*it).second && (*it).second->getType() != Element::null) {
         if ((*it).second && (*it).second->getType() != Element::null) {
-            element->set((*it).first, (*it).second);
+            if (((*it).second->getType() == Element::map) &&
+                element->contains((*it).first)) {
+                // Sub-element is a map and is also in the original config,
+                // so we need to merge them too.
+                boost::shared_ptr<MapElement> merged_map(new MapElement());
+                ConstElementPtr orig_map = element->get((*it).first);
+                ConstElementPtr other_map = (*it).second;
+                if (orig_map->getType() ==  Element::map) {
+                    merged_map->setValue(orig_map->mapValue());
+                }
+
+                // Now go recursive to merge the map sub-elements.
+                merge(merged_map, other_map);
+                element->set((*it).first, merged_map);
+            }
+            else {
+                element->set((*it).first, (*it).second);
+            }
         } else if (element->contains((*it).first)) {
         } else if (element->contains((*it).first)) {
             element->remove((*it).first);
             element->remove((*it).first);
         }
         }

+ 41 - 14
src/lib/cc/tests/data_unittests.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2009-2014  Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -848,7 +848,7 @@ TEST(Element, merge) {
     ElementPtr a = Element::createMap();
     ElementPtr a = Element::createMap();
     ElementPtr b = Element::createMap();
     ElementPtr b = Element::createMap();
     ConstElementPtr c = Element::createMap();
     ConstElementPtr c = Element::createMap();
-    merge(a, b);
+    ASSERT_NO_THROW(merge(a, b));
     EXPECT_EQ(*a, *c);
     EXPECT_EQ(*a, *c);
 
 
     a = Element::fromJSON("1");
     a = Element::fromJSON("1");
@@ -858,75 +858,102 @@ TEST(Element, merge) {
     a = Element::createMap();
     a = Element::createMap();
     b = Element::fromJSON("{ \"a\": 1 }");
     b = Element::fromJSON("{ \"a\": 1 }");
     c = Element::fromJSON("{ \"a\": 1 }");
     c = Element::fromJSON("{ \"a\": 1 }");
-    merge(a, b);
+    ASSERT_NO_THROW(merge(a, b));
     EXPECT_EQ(*a, *c);
     EXPECT_EQ(*a, *c);
 
 
     a = Element::createMap();
     a = Element::createMap();
     b = Element::fromJSON("{ \"a\": 1 }");
     b = Element::fromJSON("{ \"a\": 1 }");
     c = Element::fromJSON("{ \"a\": 1 }");
     c = Element::fromJSON("{ \"a\": 1 }");
-    merge(b, a);
+    ASSERT_NO_THROW(merge(b, a));
     EXPECT_EQ(*b, *c);
     EXPECT_EQ(*b, *c);
 
 
     a = Element::fromJSON("{ \"a\": 1 }");
     a = Element::fromJSON("{ \"a\": 1 }");
     b = Element::fromJSON("{ \"a\": 2 }");
     b = Element::fromJSON("{ \"a\": 2 }");
     c = Element::fromJSON("{ \"a\": 2 }");
     c = Element::fromJSON("{ \"a\": 2 }");
-    merge(a, b);
+    ASSERT_NO_THROW(merge(a, b));
     EXPECT_EQ(*a, *c);
     EXPECT_EQ(*a, *c);
 
 
     a = Element::fromJSON("{ \"a\": 1 }");
     a = Element::fromJSON("{ \"a\": 1 }");
     b = Element::fromJSON("{ \"a\": 2 }");
     b = Element::fromJSON("{ \"a\": 2 }");
     c = Element::fromJSON("{ \"a\": 1 }");
     c = Element::fromJSON("{ \"a\": 1 }");
-    merge(b, a);
+    ASSERT_NO_THROW(merge(b, a));
     EXPECT_EQ(*b, *c);
     EXPECT_EQ(*b, *c);
 
 
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     b = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
     b = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
     c = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
     c = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
-    merge(a, b);
+    ASSERT_NO_THROW(merge(a, b));
     EXPECT_EQ(*a, *c);
     EXPECT_EQ(*a, *c);
 
 
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     b = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
     b = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
     c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
-    merge(b, a);
+    ASSERT_NO_THROW(merge(b, a));
     EXPECT_EQ(*b, *c);
     EXPECT_EQ(*b, *c);
 
 
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     b = Element::fromJSON("{ \"a\": null }");
     b = Element::fromJSON("{ \"a\": null }");
     c = Element::fromJSON("{  }");
     c = Element::fromJSON("{  }");
-    merge(a, b);
+    ASSERT_NO_THROW(merge(a, b));
     EXPECT_EQ(*a, *c);
     EXPECT_EQ(*a, *c);
 
 
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     b = Element::fromJSON("{ \"a\": null }");
     b = Element::fromJSON("{ \"a\": null }");
     c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
-    merge(b, a);
+    ASSERT_NO_THROW(merge(b, a));
     EXPECT_EQ(*b, *c);
     EXPECT_EQ(*b, *c);
 
 
     // And some tests with multiple values
     // And some tests with multiple values
     a = Element::fromJSON("{ \"a\": 1, \"b\": true, \"c\": null }");
     a = Element::fromJSON("{ \"a\": 1, \"b\": true, \"c\": null }");
     b = Element::fromJSON("{ \"a\": 1, \"b\": null, \"c\": \"a string\" }");
     b = Element::fromJSON("{ \"a\": 1, \"b\": null, \"c\": \"a string\" }");
     c = Element::fromJSON("{ \"a\": 1, \"c\": \"a string\" }");
     c = Element::fromJSON("{ \"a\": 1, \"c\": \"a string\" }");
-    merge(a, b);
+    ASSERT_NO_THROW(merge(a, b));
     EXPECT_EQ(*a, *c);
     EXPECT_EQ(*a, *c);
 
 
     a = Element::fromJSON("{ \"a\": 1, \"b\": true, \"c\": null }");
     a = Element::fromJSON("{ \"a\": 1, \"b\": true, \"c\": null }");
     b = Element::fromJSON("{ \"a\": 1, \"b\": null, \"c\": \"a string\" }");
     b = Element::fromJSON("{ \"a\": 1, \"b\": null, \"c\": \"a string\" }");
     c = Element::fromJSON("{ \"a\": 1, \"b\": true }");
     c = Element::fromJSON("{ \"a\": 1, \"b\": true }");
-    merge(b, a);
+    ASSERT_NO_THROW(merge(b, a));
     EXPECT_EQ(*b, *c);
     EXPECT_EQ(*b, *c);
 
 
     a = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
     a = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
     b = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
     b = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
     c = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
     c = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
-    merge(a, b);
+    ASSERT_NO_THROW(merge(a, b));
     EXPECT_EQ(*a, *c);
     EXPECT_EQ(*a, *c);
 
 
     a = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
     a = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
     b = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
     b = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
     c = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
     c = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
-    merge(b, a);
+    ASSERT_NO_THROW(merge(b, a));
     EXPECT_EQ(*b, *c);
     EXPECT_EQ(*b, *c);
 
 
+    // Map sub-elements: original map element is null
+    a = Element::fromJSON("{ \"a\": 1, \"m\": null }");
+    b = Element::fromJSON("{ \"a\": 3, \"m\": {  \"b\": 9 } }");
+    c = Element::fromJSON("{ \"a\": 3, \"m\": {  \"b\": 9 } }");
+    ASSERT_NO_THROW(merge(a, b));
+    EXPECT_EQ(*a, *c);
+
+    // Map sub-elements new map element has less elements than original
+    a = Element::fromJSON("{ \"a\": 1, \"m\": { \"b\": 2, \"c\": 3 } }");
+    b = Element::fromJSON("{ \"a\": 3, \"m\": { \"b\": 9 } }");
+    c = Element::fromJSON("{ \"a\": 3, \"m\": { \"b\": 9, \"c\": 3 } }");
+    ASSERT_NO_THROW(merge(a, b));
+    EXPECT_EQ(*a, *c);
+
+    // Map sub-elements new map element is null
+    a = Element::fromJSON("{ \"a\": 1, \"m\": { \"b\": 2, \"c\": 3 } }");
+    b = Element::fromJSON("{ \"a\": 3, \"m\": null }");
+    c = Element::fromJSON("{ \"a\": 3 }");
+    ASSERT_NO_THROW(merge(a, b));
+    EXPECT_EQ(*a, *c);
+
+    // Map sub-elements new map element has more elments than origina
+    a = Element::fromJSON("{ \"a\": 1, \"m\": { \"b\": 2, \"c\": 3 } }");
+    b = Element::fromJSON("{ \"a\": 3, \"m\": { \"b\": 2, \"c\": 3, \"d\": 4} }");
+    c = Element::fromJSON("{ \"a\": 3, \"m\": { \"b\": 2, \"c\": 3, \"d\": 4} }");
+    ASSERT_NO_THROW(merge(a, b));
+    EXPECT_EQ(*a, *c);
 }
 }
 }
 }