From 40f6ac7c477f8b1b7826a133522245ba1c1a607c Mon Sep 17 00:00:00 2001 From: reuk Date: Thu, 28 Jan 2021 15:22:31 +0000 Subject: [PATCH] XmlElement: Add range-for-compatible iterators --- modules/juce_core/xml/juce_XmlElement.h | 205 +++++++++++++++++------- 1 file changed, 149 insertions(+), 56 deletions(-) diff --git a/modules/juce_core/xml/juce_XmlElement.h b/modules/juce_core/xml/juce_XmlElement.h index d15c7ac9f2..d6a6ab08c8 100644 --- a/modules/juce_core/xml/juce_XmlElement.h +++ b/modules/juce_core/xml/juce_XmlElement.h @@ -23,61 +23,6 @@ namespace juce { -//============================================================================== -/** A handy macro to make it easy to iterate all the child elements in an XmlElement. - - The parentXmlElement should be a reference to the parent XML, and the childElementVariableName - will be the name of a pointer to each child element. - - E.g. @code - XmlElement* myParentXml = createSomeKindOfXmlDocument(); - - forEachXmlChildElement (*myParentXml, child) - { - if (child->hasTagName ("FOO")) - doSomethingWithXmlElement (child); - } - - @endcode - - @see forEachXmlChildElementWithTagName -*/ -#define forEachXmlChildElement(parentXmlElement, childElementVariableName) \ -\ - for (auto* childElementVariableName = (parentXmlElement).getFirstChildElement(); \ - childElementVariableName != nullptr; \ - childElementVariableName = childElementVariableName->getNextElement()) - -/** A macro that makes it easy to iterate all the child elements of an XmlElement - which have a specified tag. - - This does the same job as the forEachXmlChildElement macro, but only for those - elements that have a particular tag name. - - The parentXmlElement should be a reference to the parent XML, and the childElementVariableName - will be the name of a pointer to each child element. The requiredTagName is the - tag name to match. - - E.g. @code - XmlElement* myParentXml = createSomeKindOfXmlDocument(); - - forEachXmlChildElementWithTagName (*myParentXml, child, "MYTAG") - { - // the child object is now guaranteed to be a element.. - doSomethingWithMYTAGElement (child); - } - - @endcode - - @see forEachXmlChildElement -*/ -#define forEachXmlChildElementWithTagName(parentXmlElement, childElementVariableName, requiredTagName) \ -\ - for (auto* childElementVariableName = (parentXmlElement).getChildByName (requiredTagName); \ - childElementVariableName != nullptr; \ - childElementVariableName = childElementVariableName->getNextElementWithTagName (requiredTagName)) - - //============================================================================== /** Used to build a tree of elements representing an XML document. @@ -695,6 +640,102 @@ public: /** Checks if a given string is a valid XML name */ static bool isValidXmlName (StringRef possibleName) noexcept; +private: + //============================================================================== + struct GetNextElement + { + XmlElement* getNext (const XmlElement& e) const { return e.getNextElement(); } + }; + + struct GetNextElementWithTagName + { + GetNextElementWithTagName() = default; + explicit GetNextElementWithTagName (String n) : name (std::move (n)) {} + XmlElement* getNext (const XmlElement& e) const { return e.getNextElementWithTagName (name); } + + String name; + }; + + //============================================================================== + template + class Iterator : private Traits + { + public: + using difference_type = ptrdiff_t; + using value_type = XmlElement*; + using pointer = const value_type*; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + Iterator() = default; + + template + Iterator (XmlElement* e, Args&&... args) + : Traits (std::forward (args)...), element (e) {} + + Iterator begin() const { return *this; } + Iterator end() const { return Iterator{}; } + + bool operator== (const Iterator& other) const { return element == other.element; } + bool operator!= (const Iterator& other) const { return ! operator== (other); } + + reference operator*() const { return element; } + pointer operator->() const { return &element; } + + Iterator& operator++() + { + element = Traits::getNext (*element); + return *this; + } + + Iterator operator++(int) + { + auto copy = *this; + ++(*this); + return copy; + } + + private: + value_type element = nullptr; + }; + +public: + //============================================================================== + /** Allows iterating the children of an XmlElement using range-for syntax. + + @code + void doSomethingWithXmlChildren (const XmlElement& myParentXml) + { + for (auto* element : myParentXml.getChildIterator()) + doSomethingWithXmlElement (element); + } + @endcode + */ + Iterator getChildIterator() const + { + return Iterator { getFirstChildElement() }; + } + + /** Allows iterating children of an XmlElement with a specific tag using range-for syntax. + + @code + void doSomethingWithXmlChildren (const XmlElement& myParentXml) + { + for (auto* element : myParentXml.getChildWithTagNameIterator ("MYTAG")) + doSomethingWithXmlElement (element); + } + @endcode + */ + Iterator getChildWithTagNameIterator (const String& name) const + { + return Iterator { getChildByName (name), name }; + } + + /** This allows us to trigger a warning inside deprecated macros. */ + #ifndef DOXYGEN + JUCE_DEPRECATED_WITH_BODY (void macroBasedForLoop() const noexcept, {}) + #endif + //============================================================================== /** This has been deprecated in favour of the toString() method. */ JUCE_DEPRECATED (String createDocument (StringRef dtdToUse, @@ -717,8 +758,8 @@ public: StringRef encodingType = "UTF-8", int lineWrapLength = 60) const); - //============================================================================== private: + //============================================================================== struct XmlAttributeNode { XmlAttributeNode (const XmlAttributeNode&) noexcept; @@ -758,4 +799,56 @@ private: JUCE_LEAK_DETECTOR (XmlElement) }; +//============================================================================== +/** DEPRECATED: A handy macro to make it easy to iterate all the child elements in an XmlElement. + + New code should avoid this macro, and instead use getChildIterator directly. + + The parentXmlElement should be a reference to the parent XML, and the childElementVariableName + will be the name of a pointer to each child element. + + E.g. @code + XmlElement* myParentXml = createSomeKindOfXmlDocument(); + + forEachXmlChildElement (*myParentXml, child) + { + if (child->hasTagName ("FOO")) + doSomethingWithXmlElement (child); + } + + @endcode + + @see forEachXmlChildElementWithTagName +*/ +#define forEachXmlChildElement(parentXmlElement, childElementVariableName) \ + for (auto* (childElementVariableName) : ((parentXmlElement).macroBasedForLoop(), (parentXmlElement).getChildIterator())) + +/** DEPRECATED: A macro that makes it easy to iterate all the child elements of an XmlElement + which have a specified tag. + + New code should avoid this macro, and instead use getChildWithTagNameIterator directly. + + This does the same job as the forEachXmlChildElement macro, but only for those + elements that have a particular tag name. + + The parentXmlElement should be a reference to the parent XML, and the childElementVariableName + will be the name of a pointer to each child element. The requiredTagName is the + tag name to match. + + E.g. @code + XmlElement* myParentXml = createSomeKindOfXmlDocument(); + + forEachXmlChildElementWithTagName (*myParentXml, child, "MYTAG") + { + // the child object is now guaranteed to be a element.. + doSomethingWithMYTAGElement (child); + } + + @endcode + + @see forEachXmlChildElement +*/ +#define forEachXmlChildElementWithTagName(parentXmlElement, childElementVariableName, requiredTagName) \ + for (auto* (childElementVariableName) : ((parentXmlElement).macroBasedForLoop(), (parentXmlElement).getChildWithTagNameIterator ((requiredTagName)))) + } // namespace juce