[FR] a recursive option for ValueTree::getChildWithName()

It would be handy to have an extra optional ‘recursive’ argument for those:

ValueTree::getChildWithName()
ValueTree::getOrCreateChildWithName()
ValueTree::getChildWithProperty()

so that if the child isn’t found in the first level, it then searches at the grandchildren level etc.

I took a stab at it:

diff --git a/modules/juce_data_structures/values/juce_ValueTree.cpp b/modules/juce_data_structures/values/juce_ValueTree.cpp
index e185d8d81..a00337937 100644
--- a/modules/juce_data_structures/values/juce_ValueTree.cpp
+++ b/modules/juce_data_structures/values/juce_ValueTree.cpp
@@ -202,32 +202,62 @@ public:
             setProperty (source.properties.getName (i), source.properties.getValueAt (i), undoManager);
     }
 
-    ValueTree getChildWithName (const Identifier& typeToMatch) const
+    ValueTree getChildWithName (const Identifier& typeToMatch, bool recursive) const
     {
         for (auto* s : children)
             if (s->type == typeToMatch)
                 return ValueTree (*s);
 
+        if (recursive)
+        {
+            for (auto* s : children)
+            {
+                auto v = s->getChildWithName(typeToMatch, true);
+                if (v.isValid())
+                    return v;
+            }
+        }
+
         return {};
     }
 
-    ValueTree getOrCreateChildWithName (const Identifier& typeToMatch, UndoManager* undoManager)
+    ValueTree getOrCreateChildWithName (const Identifier& typeToMatch, UndoManager* undoManager, bool recursive)
     {
         for (auto* s : children)
             if (s->type == typeToMatch)
                 return ValueTree (*s);
 
+        if (recursive)
+        {
+            for (auto* s : children)
+            {
+                auto v = s->getChildWithName(typeToMatch, true);
+                if (v.isValid())
+                    return v;
+            }
+        }
+
         auto newObject = new SharedObject (typeToMatch);
         addChild (newObject, -1, undoManager);
         return ValueTree (*newObject);
     }
 
-    ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
+    ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue, bool recursive = false) const
     {
         for (auto* s : children)
             if (s->properties[propertyName] == propertyValue)
                 return ValueTree (*s);
 
+        if (recursive)
+        {
+            for (auto* s : children)
+            {
+                auto v = s->getChildWithProperty(propertyName, propertyValue, true);
+                if (v.isValid())
+                    return v;
+            }
+        }
+
         return {};
     }
 
@@ -885,19 +915,19 @@ ValueTree ValueTree::Iterator::operator*() const
 ValueTree::Iterator ValueTree::begin() const noexcept   { return Iterator (*this, false); }
 ValueTree::Iterator ValueTree::end() const noexcept     { return Iterator (*this, true); }
 
-ValueTree ValueTree::getChildWithName (const Identifier& type) const
+ValueTree ValueTree::getChildWithName (const Identifier& type, bool recursive) const
 {
-    return object != nullptr ? object->getChildWithName (type) : ValueTree();
+    return object != nullptr ? object->getChildWithName (type, recursive) : ValueTree();
 }
 
-ValueTree ValueTree::getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager)
+ValueTree ValueTree::getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager, bool recursive)
 {
-    return object != nullptr ? object->getOrCreateChildWithName (type, undoManager) : ValueTree();
+    return object != nullptr ? object->getOrCreateChildWithName (type, undoManager, recursive) : ValueTree();
 }
 
-ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
+ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue, bool recursive) const
 {
-    return object != nullptr ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree();
+    return object != nullptr ? object->getChildWithProperty (propertyName, propertyValue, recursive) : ValueTree();
 }
 
 bool ValueTree::isAChildOf (const ValueTree& possibleParent) const noexcept
diff --git a/modules/juce_data_structures/values/juce_ValueTree.h b/modules/juce_data_structures/values/juce_ValueTree.h
index 0f4f725a4..c758b617f 100644
--- a/modules/juce_data_structures/values/juce_ValueTree.h
+++ b/modules/juce_data_structures/values/juce_ValueTree.h
@@ -296,7 +296,7 @@ public:
         check whether a tree is valid)
         @see getOrCreateChildWithName
     */
-    ValueTree getChildWithName (const Identifier& type) const;
+    ValueTree getChildWithName (const Identifier& type, bool recursive = false) const;
 
     /** Returns the first sub-tree with the specified type name, creating and adding
         a child with this name if there wasn't already one there.
@@ -304,7 +304,7 @@ public:
         the method on is itself invalid.
         @see getChildWithName
     */
-    ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager);
+    ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager, bool recursive = false);
 
     /** Looks for the first sub-tree that has the specified property value.
         This will scan the child trees in order, until it finds one that has property that matches
@@ -312,7 +312,7 @@ public:
         If no such tree is found, it'll return an invalid object. (You can use isValid() to
         check whether a tree is valid)
     */
-    ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const;
+    ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue, bool recursive = false) const;
 
     /** Adds a child to this tree.
         Make sure that the child being added has first been removed from any former parent before

I didn’t add anything to the documentation though.

2 Likes