//     ____   ______ __
//    / __ \ / ____// /
//   / /_/ // /    / /
//  / ____// /___ / /___   PixInsight Class Library
// /_/     \____//_____/   PCL 2.4.23
// ----------------------------------------------------------------------------
// pcl/XML.h - Released 2022-03-12T18:59:29Z
// ----------------------------------------------------------------------------
// This file is part of the PixInsight Class Library (PCL).
// PCL is a multiplatform C++ framework for development of PixInsight modules.
//
// Copyright (c) 2003-2022 Pleiades Astrophoto S.L. All Rights Reserved.
//
// Redistribution and use in both source and binary forms, with or without
// modification, is permitted provided that the following conditions are met:
//
// 1. All redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//
// 2. All redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// 3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names
//    of their contributors, may be used to endorse or promote products derived
//    from this software without specific prior written permission. For written
//    permission, please contact info@pixinsight.com.
//
// 4. All products derived from this software, in any form whatsoever, must
//    reproduce the following acknowledgment in the end-user documentation
//    and/or other materials provided with the product:
//
//    "This product is based on software from the PixInsight project, developed
//    by Pleiades Astrophoto and its contributors (https://pixinsight.com/)."
//
//    Alternatively, if that is where third-party acknowledgments normally
//    appear, this acknowledgment must be reproduced in the product itself.
//
// THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS
// INTERRUPTION; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE,
// DATA OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// ----------------------------------------------------------------------------

#ifndef __PCL_XML_h
#define __PCL_XML_h

/// \file pcl/XML.h

#include <pcl/Defs.h>

#include <pcl/Exception.h>
#include <pcl/ReferenceArray.h>
#include <pcl/String.h>

namespace pcl
{

// ----------------------------------------------------------------------------

/*!
 * \defgroup xml_parsing_and_generation XML Document Parsing and Generation
 */

// ----------------------------------------------------------------------------

class PCL_CLASS XMLDocument;
class PCL_CLASS XMLElement;

// ----------------------------------------------------------------------------

/*!
 * \class XML
 * \brief Utility functions and data for %XML document parsing and generation
 * \ingroup xml_parsing_and_generation
 */
class PCL_CLASS XML
{
public:

   /*!
    * Default constructor. This constructor is disabled because %XML is not an
    * instantiable class.
    */
   XML() = delete;

   /*!
    * Copy constructor. This constructor is disabled because %XML is not an
    * instantiable class.
    */
   XML( const XML& ) = delete;

   /*!
    * Copy assignment. This operator is disabled because %XML is not an
    * instantiable class.
    */
   XML& operator =( const XML& ) = delete;

   /*!
    * Destructor. This destructor is disabled because %XML is not an
    * instantiable class.
    */
   ~XML() = delete;

   /*!
    * Returns true iff the specified character \a c is either a white space
    * (\#x20) or a tabulator (#9) character.
    */
   template <typename T>
   static bool IsWhiteSpaceChar( T c )
   {
      return c == 0x20 || c == 9;
   }

   /*!
    * Returns true iff the specified character \a c is either a line feed
    * (\#x0A) or a carriage return (#0D) control character.
    */
   template <typename T>
   static bool IsLineBreakChar( T c )
   {
      return c == 0x0A || c == 0x0D;
   }

   /*!
    * Returns true iff the specified character \a c is an %XML space character:
    *
    * https://www.w3.org/TR/xml11/#NT-S
    */
   template <typename T>
   static bool IsSpaceChar( T c )
   {
      return IsWhiteSpaceChar( c ) || IsLineBreakChar( c );
   }

   /*!
    * Returns true iff the specified character \a c is an %XML NameStartChar:
    *
    * https://www.w3.org/TR/xml11/#NT-NameStartChar
    */
   template <typename T>
   static bool IsNameStartChar( T c )
   {
      return c >= T( 'a' ) && c <= T( 'z' )
         ||  c >= T( 'A' ) && c <= T( 'Z' )
         ||  c == T( '_' )
         ||  c == T( ':' )
         ||  c >= 0xC0    && c <= 0xD6
         ||  c >= 0xD8    && c <= 0xF6
         ||  c >= 0xF8    && c <= 0x2FF
         ||  c >= 0x370   && c <= 0x37D
         ||  c >= 0x37F   && c <= 0x1FFF
         ||  c >= 0x200C  && c <= 0x200D
         ||  c >= 0x2070  && c <= 0x218F
         ||  c >= 0x2C00  && c <= 0x2FEF
         ||  c >= 0x3001  && c <= 0xD7FF
         ||  c >= 0xF900  && c <= 0xFDCF
         ||  c >= 0xFDF0  && c <= 0xFFFD
         ||  uint32( c ) >= 0x10000 && uint32( c ) <= 0xEFFFF;
   }

   /*!
    * Returns true iff the specified character \a c is an %XML NameChar:
    *
    * https://www.w3.org/TR/xml11/#NT-NameChar
    */
   template <typename T>
   static bool IsNameChar( T c )
   {
      return IsNameStartChar( c )
         ||  c >= T( '0' ) && c <= T( '9' )
         ||  c == T( '-' )
         ||  c == T( '.' )
         ||  c == 0xB7
         ||  c >= 0x0300  && c <= 0x036F
         ||  c >= 0x203F  && c <= 0x2040;
   }

   /*!
    * Returns true iff the specified character \a c is an %XML RestrictedChar:
    *
    * https://www.w3.org/TR/xml11/#NT-RestrictedChar
    */
   template <typename T>
   static bool IsRestrictedChar( T c )
   {
      return c >= 0x00 && c <= 0x08
         ||  c >= 0x0B && c <= 0x0C
         ||  c >= 0x0E && c <= 0x1F
         ||  c >= 0x7F && c <= 0x84
         ||  c >= 0x86 && c <= 0x9F;
   }

   /*!
    * Returns true iff the specified \a name is a valid %XML qualified element
    * or attribute name:
    *
    * https://www.w3.org/TR/xml-names/#ns-qualnames
    */
   static bool IsValidName( const String& name )
   {
      if ( !name.IsEmpty() )
         if ( IsNameStartChar( *name ) )
            for ( String::const_iterator i = name.Begin(); ; )
            {
               if ( ++i == name.End() )
                  return true;
               if ( !IsNameChar( *i ) )
                  break;
            }
      return false;
   }

   /*!
    * Returns a copy of the text fragment defined by the range [i,j) of string
    * iterators with all leading and trailing space characters removed.
    */
   static String TrimmedSpaces( String::const_iterator i, String::const_iterator j );

   /*!
    * Returns a copy of the specified \a text with all leading and trailing
    * space characters removed.
    */
   static String TrimmedSpaces( const String& text );

   /*!
    * Returns a copy of the text fragment defined by the range [i,j) of string
    * iterators with all sequences of one or more space characters replaced
    * with single white space characters (\#x20).
    */
   static String CollapsedSpaces( String::const_iterator i, String::const_iterator j );

   /*!
    * Returns a copy of the specified \a text with all sequences of one or more
    * space characters replaced with single white space characters (\#x20).
    */
   static String CollapsedSpaces( const String& text );

   /*!
    * Returns a copy of the text fragment defined by the range [i,j) of string
    * iterators with all %XML references replaced by their corresponding UTF-16
    * characters.
    *
    * Both entity and character references are decoded by this function. For
    * entity references, the entire set of %XML reference names is supported:
    *
    * http://www.w3.org/TR/xml-entity-names/
    *
    * Character references are interpreted as defined in the %XML
    * specification:
    *
    * https://www.w3.org/TR/xml11/#NT-CharRef
    */
   static String DecodedText( String::const_iterator i, String::const_iterator j );

   /*!
    * Returns a copy of the specified \a text with all %XML references replaced
    * by their corresponding UTF-16 characters.
    *
    * See DecodedText( String::const_iterator, String::const_iterator ) for a
    * detailed description.
    */
   static String DecodedText( const String& text );

   /*!
    * Returns a copy of the text fragment defined by the range [i,j) of string
    * iterators with all occurences of '&', '<', '>' and '"' replaced with the
    * entity references "amp", "lt", "gt" and "quot", respectively. If \a apos
    * is true, single quotes will also be replaced with "apos" entities.
    */
   static String EncodedText( String::const_iterator i, String::const_iterator j, bool apos = true )
   {
      return EncodedText( String( i, j ), apos );
   }

   /*!
    * Returns a copy of the specified \a text with all occurences of '&', '<',
    * '>' and '"' replaced with the entity references "amp", "lt", "gt" and
    * "quot", respectively. If \a apos is true, single quotes will also be
    * replaced with "apos" entities.
    */
   static String EncodedText( const String& text, bool apos = true );

   /*!
    * Returns the Unicode value (encoded as UTF-16) corresponding to an %XML
    * reference defined by the range [i,j) of string iterators:
    *
    * https://www.w3.org/TR/xml11/#NT-Reference
    *
    * Both entity and character references are decoded by this function. For
    * entity references, the entire set of %XML reference names is supported:
    *
    * http://www.w3.org/TR/xml-entity-names/
    *
    * Character references are interpreted as defined in the %XML
    * specification:
    *
    * https://www.w3.org/TR/xml11/#NT-CharRef
    */
   static String ReferenceValue( String::const_iterator i, String::const_iterator j );

   /*!
    * Returns the Unicode value (encoded as UTF-16) corresponding to the
    * specified %XML \a reference.
    *
    * See ReferenceValue( String::const_iterator, String::const_iterator ) for
    * a detailed description.
    */
   static String ReferenceValue( const String& reference )
   {
      return ReferenceValue( reference.Begin(), reference.End() );
   }
};

// ----------------------------------------------------------------------------

/*!
 * \class XMLComponent
 * \brief Root base class of all %XML document components
 *
 * %XMLComponent supports the hierarchical structure of an %XML document by
 * implementing the basic concept of <em>parent element</em>.
 *
 * \ingroup xml_parsing_and_generation
 * \sa XMLNode
 */
class PCL_CLASS XMLComponent
{
public:

   /*!
    * Default constructor. Constructs a default %XMLComment object with no
    * parent element.
    */
   XMLComponent() = default;

   /*!
    * Copy constructor.
    */
   XMLComponent( const XMLComponent& ) = default;

   /*!
    * Returns a pointer to the parent %XML element of this component, or
    * \c nullptr if this object has no parent element.
    */
   XMLElement* ParentElement() const
   {
      return m_parent;
   }

   /*!
    * Returns true iff this is a top-level component. Top-level document
    * components have no parent elements.
    */
   bool IsTopLevel() const
   {
      return m_parent == nullptr;
   }

private:

   XMLElement* m_parent = nullptr;

   friend class XMLElement;
};

// ----------------------------------------------------------------------------

/*!
 * \namespace pcl::XMLNodeType
 * \brief     %XML document node types
 *
 * <table border="1" cellpadding="4" cellspacing="0">
 * <tr><td>XMLNodeType::Undefined</td>              <td>Undefined %XML node type.</td></tr>
 * <tr><td>XMLNodeType::ChildNode</td>              <td>Signals a child %XML document node - for internal use only.</td></tr>
 * <tr><td>XMLNodeType::Unknown</td>                <td>Represents an unsupported %XML node type.</td></tr>
 * <tr><td>XMLNodeType::Element</td>                <td>An %XML element.</td></tr>
 * <tr><td>XMLNodeType::Text</td>                   <td>A text block inside an element's contents.</td></tr>
 * <tr><td>XMLNodeType::CDATA</td>                  <td>A CDATA section.</td></tr>
 * <tr><td>XMLNodeType::ProcessingInstructions</td> <td>A processing instructions (PI) section.</td></tr>
 * <tr><td>XMLNodeType::PI</td>                     <td>A synonym to ProcessingInstructions.</td></tr>
 * <tr><td>XMLNodeType::Comment</td>                <td>A comment block.</td></tr>
 * </table>
 *
 * \ingroup xml_parsing_and_generation
 */
namespace XMLNodeType
{
   enum mask_type
   {
      Undefined =              0x00000000,
      ChildNode =              0x80000000,
      Unknown =                0x10000000,
      Element =                0x00000001,
      Text =                   0x00000002,
      CDATA =                  0x00000004,
      ProcessingInstructions = 0x00000008,
      Comment =                0x00000010
   };

   /*!
    * Returns the name of the specified %XML node \a type ("element", "text",
    * "comment", etc).
    */
   String AsString( mask_type type );
}

/*!
 * \class pcl::XMLNodeTypes
 * \brief A collection of %XML node types
 * \ingroup xml_parsing_and_generation
 */
typedef Flags<XMLNodeType::mask_type>  XMLNodeTypes;

// ----------------------------------------------------------------------------

/*!
 * \struct XMLNodeLocation
 * \brief Source code location of a parsed %XML document node
 * \ingroup xml_parsing_and_generation
 */
struct XMLNodeLocation
{
   /*!
    * Zero-based text line number where a parsed node has been identified in
    * an %XML document, or -1 if text location information is not available.
    */
   int64 line = 0;

   /*!
    * Zero-based text column number, counted from the starting character of a
    * text line, where a parsed node has been identified in an %XML document.
    * This member is -1 if text location information is not available.
    *
    * Note that the value stored in this field is actually a character index,
    * not necessarily a valid text column number. It is an actual column number
    * only if the corresponding line of text does not contain tabulator
    * characters (\#x9). If there are tabulators, there is usually no one-to-one
    * correspondence between characters and represented text columns.
    */
   int64 column = 0;

   /*!
    * Default constructor. Initializes the line and column members to -1,
    * signaling an undefined source code location.
    */
   XMLNodeLocation() = default;

   /*!
    * Constructs an %XMLNodeLocation object with the specified zero-based text
    * line and column numbers.
    */
   XMLNodeLocation( int line_, int column_ )
      : line( line_ )
      , column( column_ )
   {
   }

   /*!
    * Copy constructor.
    */
   XMLNodeLocation( const XMLNodeLocation& ) = default;

   /*!
    * Returns a string representation of this %XMLNodeLocation object in the
    * form " (line=n offset=m)", where n and m are, respectively, the one-based
    * line number and zero-based text character offset transported by this
    * object. This representation is suitable to be included directly in
    * warning or error messages generated by parsers.
    *
    * The line number token won't be generated if the text line number is
    * undefined (< 0) in this object. Similarly, the offset token won't be
    * generated if the column member is < 0. If no location information is
    * available, this member function returns an empty string.
    */
   String ToString() const;
};

// ----------------------------------------------------------------------------

/*!
 * \class XMLNode
 * \brief Abstract base class of all %XML document node classes
 *
 * %XML document nodes can be elements, text, CDATA sections, processing
 * instructions, comments, and unknown special elements. This class extends the
 * XMLComponent root base class to implement %XML document node classification
 * and serialization.
 *
 * \ingroup xml_parsing_and_generation
 */
class PCL_CLASS XMLNode : public XMLComponent
{
public:

   /*!
    * Represents the type of an %XML document node. Supported/valid node types
    * are defined in the XMLNodeType namespace.
    */
   typedef XMLNodeType::mask_type   node_type;

   /*!
    * Default constructor. Constructs a default %XMLNode object of the
    * specified \a type, with no parent element and undefined source code
    * location.
    */
   XMLNode( node_type type )
      : m_type( type )
   {
   }

   /*!
    * Copy constructor.
    *
    * The newly constructed node will be an \e orphan object, that is, it will
    * have no parent element even if the source object \a x is a child node.
    * This reflects the fact that document nodes are unique objects.
    */
   XMLNode( const XMLNode& x )
      : m_type( x.NodeType() )
      , m_location( x.m_location )
   {
   }

   /*!
    * Virtual destructor.
    */
   virtual ~XMLNode()
   {
   }

   /*!
    * Returns true iff this is a child node of an existing %XML element.
    */
   bool IsChildNode() const
   {
      return m_type.IsFlagSet( XMLNodeType::ChildNode );
   }

   /*!
    * Returns the type of this %XML document node.
    */
   node_type NodeType() const
   {
      return static_cast<node_type>( XMLNodeTypes::flag_type( m_type & unsigned( ~XMLNodeType::ChildNode ) ) );
   }

   /*!
    * Returns true iff this node is an %XML element. If this member function
    * returns true, this node can be statically casted to XMLElement.
    */
   bool IsElement() const
   {
      return NodeType() == XMLNodeType::Element;
   }

   /*!
    * Returns true iff this node represents an %XML text block. If this member
    * function returns true, this node can be statically casted to XMLText.
    */
   bool IsText() const
   {
      return NodeType() == XMLNodeType::Text;
   }

   /*!
    * Returns true iff this node represents an %XML comment. If this member
    * function returns true, this node can be statically casted to XMLComment.
    */
   bool IsComment() const
   {
      return NodeType() == XMLNodeType::Comment;
   }

   /*!
    * Returns a reference to the (immutable) source code location of this node.
    */
   const XMLNodeLocation& Location() const
   {
      return m_location;
   }

   /*!
    * Serializes this document node as an %XML fragment encoded in UTF-8.
    *
    * \param text          Reference to an 8-bit string to which the UTF-8
    *             encoded serialization of this node must be appended.
    *
    * \param autoFormat    True if line break characters (\#x0A) and
    *             indentation strings must be used to improve readability of
    *             the generated %XML code. False if no superfluous white space
    *             should be generated.
    *
    * \param indentChar    A character used for indentation of generated text
    *             lines, when \a autoFormat is true. This parameter should be
    *             either a white space (' ' or \#x20) or a tabulator ('\\t' or
    *             \#x09) character.
    *
    * \param indentSize    Number of \a indentChar characters used for each
    *             indentation level, when \a autoFormat is true.
    *
    * \param level         Recursion level. A value greater than zero denotes
    *             that this function is being called from a parent %XML
    *             element. The recursion level determines the number of
    *             \a indentChar characters prepended to each text line
    *             for indentation, when \a autoFormat is true.
    */
   virtual void Serialize( IsoString& text, bool autoFormat, char indentChar, unsigned indentSize, unsigned level ) const = 0;

   /*!
    * Returns true iff a new line character (\#x0A) can be inserted before
    * serializing this node after the specified \a previous node.
    */
   virtual bool NLAfter( const XMLNode& previous ) const;

private:

   XMLNodeTypes    m_type;
   XMLNodeLocation m_location;

   friend class XMLDocument;
   friend class XMLElement;
};

/*!
 * \class pcl::XMLNodeList
 * \brief Dynamic list of %XML node objects
 *
 * %XMLNodeList is used as the internal implementation of element child node
 * lists and document node lists. In current PCL versions, %XMLNodeList is a
 * template instantiation of ReferenceArray<> for the XMLNode class.
 *
 * \ingroup xml_parsing_and_generation
 */
typedef ReferenceArray<XMLNode>  XMLNodeList;

// ----------------------------------------------------------------------------

/*!
 * \class XMLParseError
 * \brief %XML parsing error with automatic text location information
 * generation
 *
 * The %XMLParseError is useful to generate warning and error messages during
 * document parsing tasks, with automatic generation of text location
 * information (when available) and a normalized representation of error
 * messages.
 *
 * \ingroup xml_parsing_and_generation
 */
class PCL_CLASS XMLParseError : public Error
{
public:

   /*!
    * Constructs an %XMLParseError object with a reference to an XMLNode
    * instance.
    *
    * \param node          Reference to the XMLNode object that has caused the
    *                      exception.
    *
    * \param whileDoing    Identifies the parsing action that was taking place.
    *                      For example: "Parsing Metadata child Image element".
    *
    * \param whatHappened  Describes the error that has been detected. For
    *                      example: "Missing id attribute".
    *
    * This constructor inserts node location information, if available, and
    * joins the strings appropriately to build an error message.
    */
   XMLParseError( const XMLNode& node, const String& whileDoing, const String& whatHappened )
      : Error( whileDoing + node.Location().ToString() + ": " + whatHappened )
   {
   }

   /*!
    * Constructs an %XMLParseError object with a reference to an
    * XMLNodeLocation instance.
    *
    * \param where         Reference to an XMLNodeLocation object to retrieve
    *                      text location information.
    *
    * \param whileDoing    Identifies the parsing action that was taking place.
    *                      For example: "Parsing Metadata child Image element".
    *
    * \param whatHappened  Describes the error that has been detected. For
    *                      example: "Missing id attribute".
    *
    * This constructor inserts node location information, if available, and
    * joins the strings appropriately to build an error message.
    */
   XMLParseError( const XMLNodeLocation& where, const String& whileDoing, const String& whatHappened )
      : Error( whileDoing + where.ToString() + ": " + whatHappened )
   {
   }

   /*!
    * Copy constructor.
    */
   XMLParseError( const XMLParseError& ) = default;
};

// ----------------------------------------------------------------------------

/*!
 * \class XMLAttribute
 * \brief %XML element attribute
 *
 * The %XMLAttribute class represents an element attribute, as defined by the
 * Attribute construct:
 *
 * https://www.w3.org/TR/xml11/#NT-Attribute
 *
 * \ingroup xml_parsing_and_generation
 */
class PCL_CLASS XMLAttribute : public XMLComponent
{
public:

   /*!
    * Constructs an empty %XML attribute. An empty attribute is ignored for
    * inclusion in element attribute lists.
    */
   XMLAttribute() = default;

   /*!
    * Constructs a new %XMLAttribute object with the specified qualified
    * \a name and \a value.
    *
    * The specified \a name should be a valid %XML qualified name, as defined
    * by the W3C recommendation:
    *
    * https://www.w3.org/TR/xml-names/#ns-qualnames
    *
    * However, the \a name is not checked for validity by this constructor, for
    * performance reasons. Attribute and element names are verified during the
    * document parsing and generation tasks.
    */
   XMLAttribute( const String& name, const String& value = String() )
      : m_name( name )
      , m_value( value )
   {
   }

   /*!
    * Copy constructor.
    */
   XMLAttribute( const XMLAttribute& ) = default;

   /*!
    * Returns a reference to the (immutable) qualified name of this attribute.
    */
   const String& Name() const
   {
      return m_name;
   }

   /*!
    * Returns a reference to the (immutable) value of this element attribute.
    */
   const String& Value() const
   {
      return m_value;
   }

   /*!
    * Sets a new value for this %XML element attribute.
    */
   void SetValue( const String& text )
   {
      m_value = text;
   }

   /*!
    * Returns an encoded version of the attribute value. All characters that
    * cannot legally occur in an %XML attribute value are replaced by their
    * corresponding entity references.
    */
   String EncodedValue() const
   {
      return XML::EncodedText( m_value, false/*apos*/ );
   }

   /*!
    * Equality operator.
    *
    * Two %XML element attributes are considered equal if their qualified names
    * are identical. Note that this restricts valid attribute comparisons to a
    * particular %XML document.
    */
   bool operator ==( const XMLAttribute& x ) const
   {
      return m_name == x.m_name;
   }

   /*!
    * Less-than relational operator.
    *
    * To compare %XML element attributes, only their qualified names are taken
    * into account. Note that this restricts valid attribute comparisons to a
    * particular %XML document.
    */
   bool operator <( const XMLAttribute& x ) const
   {
      return m_name < x.m_name;
   }

private:

   String m_name;
   String m_value;
};

// ----------------------------------------------------------------------------

/*!
 * \class XMLAttributeList
 * \brief Dynamic list of %XML element attributes
 *
 * %XMLAttributeList represents a sequence of %XML element attributes in a
 * start-tag, as defined in the W3C recommendation:
 *
 * https://www.w3.org/TR/xml11/#sec-starttags
 *
 * %XMLAttributeList is internally implemented as a dynamic array of
 * XMLAttribute objects.
 *
 * \ingroup xml_parsing_and_generation
 */
class PCL_CLASS XMLAttributeList
{
public:

   /*!
    * Represents the dynamic container class used internally to implement an
    * %XML element attribute list.
    */
   typedef Array<XMLAttribute>                  list_implementation;

   /*!
    * Represents a mutable %XML element attribute list iterator.
    */
   typedef list_implementation::iterator        iterator;

   /*!
    * Represents an immutable %XML element attribute list iterator.
    */
   typedef list_implementation::const_iterator  const_iterator;

   /*!
    * Constructs a new %XMLAttributeList object by parsing the specified
    * \a text string.
    *
    * The specified \a text must be a sequence of zero or more Attribute %XML
    * definitions pertaining to a start-tag:
    *
    * https://www.w3.org/TR/xml11/#NT-STag
    *
    * See the Parse() member function for a more detailed description.
    */
   XMLAttributeList( const String& text )
   {
      Parse( text );
   }

   /*!
    * Default constructor. Constructs an empty %XML attribute list.
    */
   XMLAttributeList() = default;

   /*!
    * Copy constructor.
    */
   XMLAttributeList( const XMLAttributeList& ) = default;

   /*!
    * Returns the number of element attributes in this list.
    */
   int Length() const
   {
      return int( m_list.Length() );
   }

   /*!
    * Returns true iff this attribute list is empty.
    */
   bool IsEmpty() const
   {
      return m_list.IsEmpty();
   }

   /*!
    * Returns a reference to the immutable element attribute at the specified
    * zero-based index \a i. No bounds checking is performed: if the specified
    * index is invalid this function invokes undefined behavior.
    */
   const XMLAttribute& operator []( int i ) const
   {
      return m_list[i];
   }

   /*!
    * Returns an immutable iterator located at the beginning of this element
    * attribute list.
    */
   const_iterator Begin() const
   {
      return m_list.Begin();
   }

   /*!
    * Returns an immutable iterator located at the end of this element
    * attribute list.
    */
   const_iterator End() const
   {
      return m_list.End();
   }

#ifndef __PCL_NO_STL_COMPATIBLE_ITERATORS
   /*!
    * STL-compatible iteration. Equivalent to Begin() const.
    */
   const_iterator begin() const
   {
      return Begin();
   }

   /*!
    * STL-compatible iteration. Equivalent to End() const.
    */
   const_iterator end() const
   {
      return End();
   }
#endif   // !__PCL_NO_STL_COMPATIBLE_ITERATORS

   /*!
    * Returns true iff this list contains an element attribute with the
    * specified qualified \a name.
    */
   bool HasAttribute( const String& name ) const
   {
      return m_list.Contains( name );
   }

   /*!
    * Returns the value of the element attribute with the specified qualified
    * \a name, or an empty string if this list does not contain such an element
    * attribute.
    */
   String AttributeValue( const String& name ) const
   {
      const_iterator a = m_list.Search( name );
      return (a != m_list.End()) ? a->Value() : String();
   }

   /*!
    * Causes this list to contain an %XML element attribute with the specified
    * qualified \a name and \a value.
    *
    * If an attribute with the same qualified \a name already exists in this
    * list, then its value will be changed. Otherwise, a new attribute will be
    * appended to this list.
    *
    * This member function ensures that no %XML element can have two or more
    * attributes with the same qualified name. This constraint is part of the
    * %XML specification:
    *
    * https://www.w3.org/TR/xml11/#sec-starttags
    * https://www.w3.org/TR/xml-names/#scoping-defaulting
    */
   void SetAttribute( const String& name, const String& value )
   {
      if ( !name.IsEmpty() )
      {
         iterator a = m_list.Search( name );
         if ( a == m_list.End() )
            m_list.Add( XMLAttribute( name, value ) );
         else
            a->SetValue( value );
      }
   }

   /*!
    * Causes this list to contain the specified %XML element \a attribute.
    *
    * See SetAttribute( const String&, const String& ) for more information.
    */
   void SetAttribute( const XMLAttribute& attribute )
   {
      if ( !attribute.Name().IsEmpty() )
      {
         iterator a = m_list.Search( attribute );
         if ( a == m_list.End() )
            m_list.Add( attribute );
         else
            *a = attribute;
      }
   }

   /*!
    * Insertion operator. Returns a reference to this object.
    *
    * This operator is equivalent to SetAttribute( const XMLAttribute& ).
    */
   XMLAttributeList& operator <<( const XMLAttribute& attribute )
   {
      SetAttribute( attribute );
      return *this;
   }

   /*!
    * Causes this list to contain the specified \a list of %XML element
    * attributes.
    *
    * For each attribute in the specified \a list, if an attribute with the
    * same qualified \a name already exists in this list, then its value will
    * be changed. Otherwise, a new attribute will be appended to this list.
    *
    * This member function ensures that no %XML element can have two or more
    * attributes with the same qualified name. This constraint is part of the
    * %XML specification:
    *
    * https://www.w3.org/TR/xml11/#sec-starttags
    * https://www.w3.org/TR/xml-names/#scoping-defaulting
    */
   void SetAttributes( const XMLAttributeList& list )
   {
      for ( auto a : list )
         SetAttribute( a );
   }

   /*!
    * Insertion operator. Returns a reference to this object.
    *
    * This operator is equivalent to SetAttributes( const XMLAttributeList& ).
    */
   XMLAttributeList& operator <<( const XMLAttributeList& list )
   {
      SetAttributes( list );
      return *this;
   }

   /*!
    * Removes the element attribute with the specified qualified \a name, if it
    * exists in this list. If no attribute with the specified \a name exists,
    * this member function has no effect.
    */
   void RemoveAttribute( const String& name )
   {
      iterator a = m_list.Search( name );
      if ( a != m_list.End() )
         m_list.Remove( a );
   }

   /*!
    * Removes all element attributes in this list, yielding an empty element
    * attribute list.
    */
   void Clear()
   {
      m_list.Clear();
   }

   /*!
    * Sorts the element attributes in this list in ascending order by comparing
    * their qualified names.
    */
   void Sort()
   {
      m_list.Sort();
   }

   /*!
    * Parses the specified \a text, encoded as UTF-16, to generate a new list
    * of %XML element attributes.
    *
    * The specified \a text must be a sequence of zero or more Attribute %XML
    * definitions pertaining to a start-tag, as described in the W3C
    * recommendation:
    *
    * https://www.w3.org/TR/xml11/#NT-STag
    *
    * Attribute value normalization is applied to each parsed attribute:
    *
    * https://www.w3.org/TR/xml11/#AVNormalize
    *
    * In attribute values, all entity and character references are decoded. See
    * the XML::DecodedText() static function for more information on reference
    * decoding. Normalization also implies space trimming and compression: all
    * leading and trailing space characters are removed, and all sequences of
    * one or more space characters are replaced by single white space
    * characters (\#x20).
    */
   void Parse( const String& text );

   /*!
    * Performs the %XML serialization of this element attribute list and
    * appends it to the specified \a text string, encoded in UTF-8.
    *
    * The generated serialization is a sequence of zero or more Attribute %XML
    * definitions pertaining to a start-tag, as described in the W3C
    * recommendation:
    *
    * https://www.w3.org/TR/xml11/#NT-STag
    */
   void Serialize( IsoString& text ) const;

private:

   list_implementation m_list;
};

// ----------------------------------------------------------------------------

/*!
 * \class XMLElement
 * \brief %XML element
 *
 * The %XMLElement class represents an %XML document element:
 *
 * https://www.w3.org/TR/xml11/#dt-element
 *
 * Elements are the main data holders in the logical design of %XML, following
 * a hierarchical tree structure.
 *
 * \ingroup xml_parsing_and_generation
 */
class PCL_CLASS XMLElement : public XMLNode
{
public:

   /*!
    * Represents a mutable child node list iterator.
    */
   typedef XMLNodeList::iterator          iterator;

   /*!
    * Represents an immutable child node list iterator.
    */
   typedef XMLNodeList::const_iterator    const_iterator;

   /*!
    * A list of child %XML elements. Implemented as a template instantiation of
    * ReferenceArray<> for the XMLElement class.
    */
   typedef ReferenceArray<XMLElement>     child_element_list;

   /*!
    * Default constructor. Constructs an uninitialized %XMLElement structure.
    */
   XMLElement()
      : XMLNode( XMLNodeType::Element )
   {
   }

   /*!
    * Constructs an empty %XMLElement object with the specified qualified
    * \a name and \a attributes.
    */
   XMLElement( const String& name, const XMLAttributeList& attributes = XMLAttributeList() )
      : XMLNode( XMLNodeType::Element )
      , m_name( name )
      , m_attributes( attributes )
   {
   }

   /*!
    * Constructs an empty %XMLElement object with the specified qualified
    * \a name and \a attributes, as a child node of the specified \a parent
    * element.
    */
   XMLElement( XMLElement& parent, const String& name, const XMLAttributeList& attributes = XMLAttributeList() )
      : XMLNode( XMLNodeType::Element )
      , m_name( name )
      , m_attributes( attributes )
   {
      parent.AddChildNode( this );
   }

   /*!
    * Copy constructor. This constructor is disabled because %XMLElement
    * represents unique objects.
    */
   XMLElement( const XMLElement& ) = delete;

   /*!
    * Copy assignment. This operator is disabled because %XMLElement represents
    * unique objects.
    */
   XMLElement& operator =( const XMLElement& ) = delete;

   /*!
    * Virtual destructor. If this element contains child nodes, all of them
    * will be destroyed recursively.
    */
   virtual ~XMLElement()
   {
      DestroyChildNodes();
   }

   /*!
    * Returns true iff this is a root %XML element. A root %XML element has no
    * parent element.
    *
    * Note that this member function can return true in two different
    * situations: when the element has been generated during a document parsing
    * process (in which case this is an actual document root element), and if
    * this object has not been initialized yet (because it has been newly
    * constructed and still has not been associated with an %XML document).
    */
   bool IsRootElement() const
   {
      return ParentElement() == nullptr;
   }

   /*!
    * Returns a reference to the (immutable) qualified element name.
    */
   const String& Name() const
   {
      return m_name;
   }

   /*!
    * Returns a copy of the list of %XML element attributes.
    */
   XMLAttributeList Attributes() const
   {
      return m_attributes;
   }

   /*!
    * Returns true iff this element has one or more attributes defined.
    */
   bool HasAttributes() const
   {
      return !m_attributes.IsEmpty();
   }

   /*!
    * Returns true iff this element has an attribute with the specified
    * qualified \a name.
    */
   bool HasAttribute( const String& name ) const
   {
      return m_attributes.HasAttribute( name );
   }

   /*!
    * Returns the value of the attribute with the specified qualified \a name
    * in this element, or an empty string if this element has no such
    * attribute.
    */
   String AttributeValue( const String& name ) const
   {
      return m_attributes.AttributeValue( name );
   }

   /*!
    * Causes this %XML element to contain an attribute with the specified
    * qualified \a name and \a value.
    *
    * If an attribute with the same qualified \a name already exists in this
    * element, then its value will be changed. Otherwise, a new attribute will
    * be created in this element.
    *
    * This member function ensures that no %XML element can have two or more
    * attributes with the same qualified name. This constraint is part of the
    * %XML specification:
    *
    * https://www.w3.org/TR/xml11/#sec-starttags
    * https://www.w3.org/TR/xml-names/#scoping-defaulting
    */
   void SetAttribute( const String& name, const String& value )
   {
      XMLAttribute a( name, value );
      a.m_parent = this;
      m_attributes.SetAttribute( a );
   }

   /*!
    * Causes this %XML element to contain the specified \a attribute.
    *
    * See SetAttribute( const String&, const String& ) for more information.
    */
   void SetAttribute( const XMLAttribute& attribute )
   {
      XMLAttribute a( attribute );
      a.m_parent = this;
      m_attributes.SetAttribute( a );
   }

   /*!
    * Insertion operator. Returns a reference to this object.
    *
    * This operator is equivalent to SetAttribute( const XMLAttribute& ).
    */
   XMLElement& operator <<( const XMLAttribute& attribute )
   {
      SetAttribute( attribute );
      return *this;
   }

   /*!
    * Causes this %XML element to contain the specified \a list of attributes.
    *
    * For each attribute in the specified \a list, if an attribute with the
    * same qualified \a name already exists in this element, then its value
    * will be changed. Otherwise, a new attribute will be created in this
    * element.
    *
    * This member function ensures that no %XML element can have two or more
    * attributes with the same qualified name. This constraint is part of the
    * %XML specification:
    *
    * https://www.w3.org/TR/xml11/#sec-starttags
    * https://www.w3.org/TR/xml-names/#scoping-defaulting
    */
   void SetAttributes( const XMLAttributeList& list )
   {
      for ( auto a : list )
         SetAttribute( a );
   }

   /*!
    * Insertion operator. Returns a reference to this object.
    *
    * This operator is equivalent to SetAttributes( const XMLAttributeList& ).
    */
   XMLElement& operator <<( const XMLAttributeList& list )
   {
      SetAttributes( list );
      return *this;
   }

   /*!
    * Removes the attribute with the specified qualified \a name, if it exists
    * in this element. If this element has no attribute with the specified
    * \a name, this member function has no effect.
    */
   void RemoveAttribute( const String& name )
   {
      m_attributes.RemoveAttribute( name );
   }

   /*!
    * Removes all existing attributes in this element.
    */
   void ClearAttributes()
   {
      m_attributes.Clear();
   }

   /*!
    * Sorts the existing attributes in this element in ascending order by
    * comparing their qualified names.
    */
   void SortAttributes()
   {
      m_attributes.Sort();
   }

   /*!
    * Sorts the existing attributes in this element in ascending order.
    * Ordering of elements is defined such that for any pair a, b of
    * XMLAttribute objects in this element, the binary predicate p(a,b) is true
    * iff a precedes b.
    */
   template <class BP>
   void SortAttributes( BP p )
   {
      m_attributes.Sort( p );
   }

   /*!
    * Parses the specified \a text, encoded as UTF-16, to generate a new list
    * of attributes in this %XML element. The previous list of attributes, if
    * any, will be replaced by the newly generated list.
    *
    * The specified \a text must be a sequence of zero or more Attribute %XML
    * definitions pertaining to a start-tag, as described in the W3C
    * recommendation:
    *
    * https://www.w3.org/TR/xml11/#NT-STag
    */
   void ParseAttributes( const String& text )
   {
      XMLAttributeList list( text );
      ClearAttributes();
      SetAttributes( list );
   }

   /*!
    * Performs the %XML serialization of the attribute list in this element and
    * appends it to the specified \a text string, encoded in UTF-8.
    *
    * The generated serialization is a sequence of zero or more Attribute %XML
    * definitions pertaining to a start-tag, as described in the W3C
    * recommendation:
    *
    * https://www.w3.org/TR/xml11/#NT-STag
    */
   void SerializeAttributes( IsoString& text ) const
   {
      m_attributes.Serialize( text );
   }

   /*!
    * Returns the number of child nodes in this %XML element.
    */
   int ChildCount() const
   {
      return int( m_childNodes.Length() );
   }

   /*!
    * Returns true iff this is an empty %XML element. An empty element has no
    * child nodes.
    */
   bool IsEmpty() const
   {
      return m_childNodes.IsEmpty();
   }

   /*!
    * Returns a reference to the immutable child node at the specified
    * zero-based index \a i. No bounds checking is performed: if the specified
    * index is invalid this function invokes undefined behavior.
    */
   const XMLNode& operator []( int i ) const
   {
      return m_childNodes[i];
   }

   /*!
    * Returns a reference to the immutable first child node in this element. No
    * bounds checking is performed: if this element is empty, this function
    * invokes undefined behavior.
    */
   const XMLNode& First() const
   {
      return m_childNodes.First();
   }

   /*!
    * Returns a reference to the immutable last child node in this element. No
    * bounds checking is performed: if this element is empty, this function
    * invokes undefined behavior.
    */
   const XMLNode& Last() const
   {
      return m_childNodes.Last();
   }

   /*!
    * Returns an immutable iterator located at the beginning of the list of
    * child nodes of this %XML element.
    */
   const_iterator Begin() const
   {
      return m_childNodes.Begin();
   }

   /*!
    * Returns an immutable iterator located at the end of the list of child
    * nodes of this %XML element.
    */
   const_iterator End() const
   {
      return m_childNodes.End();
   }

#ifndef __PCL_NO_STL_COMPATIBLE_ITERATORS
   /*!
    * STL-compatible iteration. Equivalent to Begin() const.
    */
   const_iterator begin() const
   {
      return Begin();
   }

   /*!
    * STL-compatible iteration. Equivalent to End() const.
    */
   const_iterator end() const
   {
      return End();
   }
#endif   // !__PCL_NO_STL_COMPATIBLE_ITERATORS

#ifndef __PCL_NO_MUTABLE_XML_ELEMENT_ITERATORS

   /*!
    * Returns a mutable iterator located at the beginning of the list of child
    * nodes of this %XML element.
    */
   iterator Begin()
   {
      return m_childNodes.Begin();
   }

   /*!
    * Returns a mutable iterator located at the end of the list of child nodes
    * of this %XML element.
    */
   iterator End()
   {
      return m_childNodes.End();
   }

   /*!
    * Returns an immutable iterator located at the beginning of the list of
    * child nodes of this %XML element.
    */
   const_iterator ConstBegin() const
   {
      return m_childNodes.ConstBegin();
   }

   /*!
    * Returns an immutable iterator located at the end of the list of child
    * nodes of this %XML element.
    */
   const_iterator ConstEnd() const
   {
      return m_childNodes.ConstEnd();
   }

# ifndef __PCL_NO_STL_COMPATIBLE_ITERATORS
   /*!
    * STL-compatible iteration. Equivalent to Begin().
    */
   iterator begin()
   {
      return Begin();
   }

   /*!
    * STL-compatible iteration. Equivalent to End().
    */
   iterator end()
   {
      return End();
   }
# endif   // !__PCL_NO_STL_COMPATIBLE_ITERATORS

#endif   // !__PCL_NO_MUTABLE_XML_ELEMENT_ITERATORS

   /*!
    * Returns true iff this element contains one or more child %XML elements.
    */
   bool HasElements() const
   {
      return m_childTypes.IsFlagSet( XMLNodeType::Element );
   }

   /*!
    * Returns true iff this element contains one or more child text blocks.
    */
   bool HasText() const
   {
      return m_childTypes.IsFlagSet( XMLNodeType::Text );
   }

   /*!
    * Returns true iff this element contains one or more child CDATA sections.
    */
   bool HasCDATA() const
   {
      return m_childTypes.IsFlagSet( XMLNodeType::CDATA );
   }

   /*!
    * Returns true iff this element contains one or more child processing
    * instructions.
    */
   bool HasProcessingInstructions() const
   {
      return m_childTypes.IsFlagSet( XMLNodeType::ProcessingInstructions );
   }

   /*!
    * Returns true iff this element contains one or more child comment
    * sections.
    */
   bool HasComments() const
   {
      return m_childTypes.IsFlagSet( XMLNodeType::Comment );
   }

   /*!
    * Returns the text contents of this element, or an empty string if this
    * element has no text child nodes.
    *
    * If this element has two or more text child nodes, the returned value is
    * the concatenation of all child text nodes.
    */
   String Text() const;

   /*!
    * \internal
    */
   void GetChildElements( child_element_list& list, bool recursive ) const
   {
      for ( const XMLNode& node : m_childNodes )
         if ( node.IsElement() )
         {
            const XMLElement& element = static_cast<const XMLElement&>( node );
            list << &element;
            if ( recursive )
               element.GetChildElements( list, recursive );
         }
   }

   /*!
    * Returns a list with all child elements of this element.
    *
    * if \a recursive is \c true, this member function performs a recursive
    * search across the entire tree structure rooted at this element. Otherwise
    * only the direct descendant elements will be returned.
    */
   child_element_list ChildElements( bool recursive = false ) const
   {
      child_element_list list;
      GetChildElements( list, recursive );
      return list;
   }

   /*!
    * \internal
    */
   void GetChildElementsByName( child_element_list& list, const String& name, bool recursive ) const
   {
      for ( const XMLNode& node : m_childNodes )
         if ( node.IsElement() )
         {
            const XMLElement& element = static_cast<const XMLElement&>( node );
            if ( element.Name() == name )
            {
               list << &element;
               if ( recursive )
                  element.GetChildElementsByName( list, name, recursive );
            }
         }
   }

   /*!
    * Returns a list with all child elements of this element with the specified
    * \a name.
    *
    * if \a recursive is \c true, this member function performs a recursive
    * search across the entire tree structure rooted at this element. Otherwise
    * only the direct descendant elements will be returned.
    */
   child_element_list ChildElementsByName( const String& name, bool recursive = false ) const
   {
      child_element_list list;
      GetChildElementsByName( list, name, recursive );
      return list;
   }

   /*!
    * \internal
    */
   void GetChildNodesByType( XMLNodeList& list, XMLNodeTypes types, bool recursive ) const
   {
      for ( const XMLNode& node : m_childNodes )
         if ( types.IsFlagSet( node.NodeType() ) )
         {
            list << &node;
            if ( recursive )
               if ( node.IsElement() )
                  static_cast<const XMLElement&>( node ).GetChildNodesByType( list, types, recursive );
         }
   }

   /*!
    * Returns a list with all child nodes of this element of the specified
    * \a types. The \a types argument can be an ORed combination of XMLNodeType
    * enumerated mask values.
    *
    * if \a recursive is \c true, this member function performs a recursive
    * search across the entire tree structure rooted at this element. Otherwise
    * only the direct descendant nodes will be returned.
    */
   XMLNodeList ChildNodesByType( XMLNodeTypes types, bool recursive = false ) const
   {
      XMLNodeList list;
      GetChildNodesByType( list, types, recursive );
      return list;
   }

   /*!
    * \internal
    */
   template <class UP>
   void GetChildNodesThat( XMLNodeList& list, UP u, bool recursive ) const
   {
      for ( const XMLNode& node : m_childNodes )
         if ( u( node ) )
         {
            list << &node;
            if ( recursive )
               if ( node.IsElement() )
                  static_cast<const XMLElement&>( node ).GetChildNodesThat( list, u, recursive );
         }
   }

   /*!
    * Returns a list with all child nodes of this element that satisfy the
    * specified unary predicate \a u.
    *
    * For each child node n in this element, n will be included in the returned
    * list iff u( n ) returns true.
    *
    * if \a recursive is \c true, this member function performs a recursive
    * search across the entire tree structure rooted at this element. Otherwise
    * only the direct descendant nodes will be returned.
    */
   template <class UP>
   XMLNodeList ChildNodesThat( UP u, bool recursive = false ) const
   {
      XMLNodeList list;
      GetChildNodesThat( list, u, recursive );
      return list;
   }

   /*!
    * Appends a child \a node to this %XML element.
    *
    * The specified \a node will be owned by this element, which will destroy
    * it automatically (and recursively) upon destruction.
    */
   void AddChildNode( XMLNode* node )
   {
      m_childNodes << node;
      node->m_parent = this;
      node->m_type.SetFlag( XMLNodeType::ChildNode );
      m_childTypes.SetFlag( node->NodeType() );
   }

   /*!
    * Insertion operator: Appends a child \a node to this %XML element. Returns
    * a reference to this object.
    *
    * This operator does the same as AddChildNode( node ).
    */
   XMLElement& operator <<( XMLNode* node )
   {
      AddChildNode( node );
      return *this;
   }

   /*!
    * Appends an ordered sequence of child \a nodes to this %XML element.
    *
    * After calling this function, all existing \a nodes in the specified list
    * will be owned by this element, which will destroy them automatically (and
    * recursively) upon destruction.
    */
   void AddChildNodes( XMLNodeList& nodes )
   {
      for ( XMLNode& node : nodes )
         AddChildNode( &node );
   }

   /*!
    * Insertion operator: Appends an ordered sequence of child \a nodes to this
    * %XML element. Returns a reference to this object.
    *
    * This operator does the same as AddChildNodes( nodes ).
    */
   XMLElement& operator <<( XMLNodeList& nodes )
   {
      AddChildNodes( nodes );
      return *this;
   }

   /*!
    * \internal
    * Appends a new child \a node to this %XML element.
    *
    * The specified \a node will be owned by this element, which will destroy
    * it automatically (and recursively) upon destruction.
    */
   void AddChildNode( XMLNode* node, const XMLNodeLocation& location )
   {
      node->m_location = location;
      AddChildNode( node );
   }

   /*!
    * Recursively destroys all existing child nodes in this %XML element,
    * yielding an empty element.
    */
   void DestroyChildNodes()
   {
      m_childNodes.Destroy();
      m_childTypes = XMLNodeType::Undefined;
   }

   /*!
    * Releases the child nodes of this %XML element.
    *
    * This function returns the (possibly empty) list of child nodes in this
    * element and causes this object to forget them. The caller will be
    * responsible for destroying and deallocating all of the returned nodes as
    * appropriate. After calling this member function, this %XML element will
    * be empty.
    */
   XMLNodeList ReleaseChildNodes()
   {
      XMLNodeList nodes = m_childNodes;
      m_childNodes.Clear();
      m_childTypes = XMLNodeType::Undefined;
      return nodes;
   }

   /*!
    * Recursively serializes this %XML element and its contents. Appends the
    * generated %XML source code to the specified 8-bit \a text string, encoded
    * in UTF-8.
    *
    * See XMLNode::Serialize() for information on function parameters.
    */
   void Serialize( IsoString& text, bool autoFormat, char indentChar, unsigned indentSize, unsigned level ) const override;

private:

   String           m_name;
   XMLAttributeList m_attributes;
   XMLNodeList      m_childNodes;
   XMLNodeTypes     m_childTypes = XMLNodeType::Undefined;
};

// ----------------------------------------------------------------------------

/*!
 * \class pcl::XMLElementList
 * \brief Dynamic list of %XML elements
 *
 * %XMLElementList is a template instantiation of ReferenceArray<> for the
 * XMLElement class. It is used to transport ordered sequences of child element
 * nodes. See for example XMLElement::ChildElements().
 *
 * \ingroup xml_parsing_and_generation
 */
typedef XMLElement::child_element_list XMLElementList;

// ----------------------------------------------------------------------------

/*!
 * \class XMLText
 * \brief %XML text block
 *
 * This %XMLText class represents a text entity in an %XML document:
 *
 * https://www.w3.org/TR/xml11/#dt-text
 *
 * \ingroup xml_parsing_and_generation
 */
class PCL_CLASS XMLText : public XMLNode
{
public:

   /*!
    * Constructs a new %XMLText object.
    *
    * \param text    The Unicode text block contents encoded as UTF-16.
    *
    * \param preserveSpaces   If false, the text block will be transformed by
    *                trimming and collapsing spaces: All leading and trailing
    *                space characters will be removed, and all sequences of one
    *                or more space characters will be replaced by single white
    *                space characters (\#x20). If true, the specified \a text
    *                string will be stored intact.
    *
    * \param verbatim   If true, the text block will be serialized unencoded,
    *                that is, exactly as is being specified, in the XML
    *                document. No codification of illegal characers such as
    *                quotes and '<' or '>' will be performed. This is useful to
    *                generate special code blocks that must be included
    *                literally, such as &lt;style&gt; or &lt;script&gt; tags in
    *                HTML and SVG documents.
    *
    * Besides text contents transformation, space preservation also has an
    * impact in the way text blocks are serialized as %XML: New line characters
    * (\#x0A) are never used to separate text blocks from their parent or
    * sibling nodes when space preservation is enabled.
    */
   XMLText( const String& text, bool preserveSpaces = true, bool verbatim = false )
      : XMLNode( XMLNodeType::Text )
      , m_text( preserveSpaces ? text : XML::CollapsedSpaces( XML::TrimmedSpaces( text ) ) )
      , m_preserveSpaces( preserveSpaces )
      , m_verbatim( verbatim )
   {
   }

   /*!
    * Copy constructor.
    */
   XMLText( const XMLText& ) = default;

   /*!
    * Returns a reference to the (immutable) text string contained by this %XML
    * text block. The returned string is encoded in UTF-16.
    */
   const String& Text() const
   {
      return m_text;
   }

   /*!
    * Returns true iff this text block preserves space characters for
    * serialization. See the class constructor for more information.
    */
   bool IsPreserveSpaces() const
   {
      return m_preserveSpaces;
   }

   /*!
    * Returns an encoded version of this text block. All characters that cannot
    * legally occur in an %XML text block are replaced by their corresponding
    * entity references.
    */
   String EncodedText() const
   {
      return XML::EncodedText( m_text );
   }

   /*!
    * Returns a space-transformed version of this text block.
    *
    * \param collapse   Replace all sequences of one or more space characters
    *                   with single white space characters (\#x20).
    *
    * \param trim       Remove all leading and trailing space characters.
    */
   String SpaceTransformedText( bool collapse, bool trim ) const
   {
      String text = m_text;
      if ( trim )
         text = XML::TrimmedSpaces( text );
      if ( collapse )
         text = XML::CollapsedSpaces( text );
      return text;
   }

   /*!
    * Serializes this %XML text block with UTF-8 encoding.
    *
    * See XMLNode::Serialize() for information on function parameters. See also
    * the class constructor for information on space preservation options in
    * %XML text blocks.
    */
   void Serialize( IsoString& text, bool autoFormat, char indentChar, unsigned indentSize, unsigned level ) const override;

   /*!
    * Returns true iff a new line character (\#x0A) can be inserted before
    * serializing this node after the specified \a previous node.
    *
    * In the case of a text block, a new line character can only be inserted
    * if the block does not preserve space characters. If space preservation is
    * enabled, new line characters are forbidden at the beginning and end of
    * the text block serialization.
    */
   bool NLAfter( const XMLNode& previous ) const override
   {
      return !m_preserveSpaces;
   }

private:

   String m_text; // N.B.: This is plain, that is, decoded, text.
   bool   m_preserveSpaces = true;
   bool   m_verbatim = false;
};

// ----------------------------------------------------------------------------

/*!
 * \class XMLCDATA
 * \brief %XML CDATA section
 *
 * The %XMLCDATA class represents a CDATA section in an %XML document:
 *
 * https://www.w3.org/TR/xml11/#sec-cdata-sect
 *
 * \ingroup xml_parsing_and_generation
 */
class PCL_CLASS XMLCDATA : public XMLNode
{
public:

   /*!
    * Constructs a new %XMLCDATA object with the specified character \a data
    * encoded in UTF-16.
    *
    * The specified \a data must not contain the sequence "]]>". Any occurrence
    * of this forbidden sequence will be removed for serialization.
    */
   XMLCDATA( const String& data = String() )
      : XMLNode( XMLNodeType::CDATA )
      , m_cdata( data )
   {
   }

   /*!
    * Copy constructor.
    */
   XMLCDATA( const XMLCDATA& ) = default;

   /*!
    * Returns a reference to the (immutable) character data string, encoded as
    * UTF-16, contained by this CDATA section.
    */
   const String& CData() const
   {
      return m_cdata;
   }

   /*!
    * Serializes this %XML CDATA section with UTF-8 encoding.
    *
    * See XMLNode::Serialize() for information on function parameters.
    */
   void Serialize( IsoString& text, bool autoFormat, char indentChar, unsigned indentSize, unsigned level ) const override;

private:

   String m_cdata;
};

// ----------------------------------------------------------------------------

/*!
 * \class XMLProcessingInstructions
 * \brief %XML processing instructions
 *
 * The %XMLProcessingInstructions class represents a processing instructions
 * (PI) tag in an %XML document:
 *
 * https://www.w3.org/TR/xml11/#sec-pi
 *
 * \ingroup xml_parsing_and_generation
 */
class XMLProcessingInstructions : public XMLNode
{
public:

   /*!
    * Constructs a new %XMLProcessingInstructions object with the specified
    * \a target name and \a instructions string, both encoded in UTF-16.
    *
    * The specified \a instructions string must not contain the sequence "?>".
    * Any occurrence of this forbidden sequence will be removed for
    * serialization.
    */
   XMLProcessingInstructions( const String& target, const String& instructions )
      : XMLNode( XMLNodeType::ProcessingInstructions )
      , m_target( target )
      , m_instructions( instructions )
   {
   }

   /*!
    * Copy constructor.
    */
   XMLProcessingInstructions( const XMLProcessingInstructions& ) = default;

   /*!
    * Returns a reference to the (immutable) instructions target name.
    */
   const String& Target() const
   {
      return m_target;
   }

   /*!
    * Returns a reference to the (immutable) instructions string.
    */
   const String& Instructions() const
   {
      return m_instructions;
   }

   /*!
    * Serializes this %XML PI section with UTF-8 encoding.
    *
    * See XMLNode::Serialize() for information on function parameters.
    */
   void Serialize( IsoString& text, bool autoFormat, char indentChar, unsigned indentSize, unsigned level ) const override;

private:

   String m_target;
   String m_instructions;
};

// ----------------------------------------------------------------------------

/*!
 * \class XMLComment
 * \brief %XML comment section
 *
 * The %XMLComment class represents a comment in an %XML document:
 *
 * https://www.w3.org/TR/xml11/#sec-comments
 *
 * \ingroup xml_parsing_and_generation
 */
class PCL_CLASS XMLComment : public XMLNode
{
public:

   /*!
    * Constructs a new %XMLComment object with the specified \a comment string
    * encoded in UTF-16.
    *
    * The specified \a comment must not contain the sequence "--" or end with
    * a '-' character. Any occurrence of these forbidden sequences will be
    * removed for serialization.
    */
   XMLComment( const String& comment )
      : XMLNode( XMLNodeType::Comment )
      , m_comment( comment )
   {
   }

   /*!
    * Copy constructor.
    */
   XMLComment( const XMLComment& ) = default;

   /*!
    * Returns a reference to the (immutable) comment string.
    */
   const String& Comment() const
   {
      return m_comment;
   }

   /*!
    * Serializes this %XML comment section with UTF-8 encoding.
    *
    * See XMLNode::Serialize() for information on function parameters.
    */
   void Serialize( IsoString& text, bool autoFormat, char indentChar, unsigned indentSize, unsigned level ) const override;

private:

   String m_comment;
};

// ----------------------------------------------------------------------------

/*!
 * \class XMLUnknownElement
 * \brief Unsupported or invalid %XML element
 *
 * %XMLUnknownElement represents an invalid or unrecognized %XML element
 * retrieved while parsing an %XML document. In the current PCL implementation,
 * an %XMLUnknownElement object is generated if the parser finds an element
 * whose start-tag begins with the "<!" token and is neither a comment section
 * nor a DOCTYPE declaration.
 *
 * \ingroup xml_parsing_and_generation
 */
class PCL_CLASS XMLUnknownElement : public XMLNode
{
public:

   /*!
    * Constructs an %XMLUnknownElement with the specified qualified \a name and
    * element \a parameters.
    */
   XMLUnknownElement( const String& name, const String& parameters = String() )
      : XMLNode( XMLNodeType::Unknown )
      , m_name( name )
      , m_parameters( parameters )
   {
   }

   /*!
    * Copy constructor.
    */
   XMLUnknownElement( const XMLUnknownElement& ) = default;

   /*!
    * Returns a reference to the (immutable) unknown element name.
    */
   const String& Name() const
   {
      return m_name;
   }

   /*!
    * Returns a reference to the (immutable) unknown element parameters.
    */
   const String& Parameters() const
   {
      return m_parameters;
   }

   /*!
    * Serializes this %XML unknown element with UTF-8 encoding.
    *
    * See XMLNode::Serialize() for information on function parameters.
    */
   void Serialize( IsoString& text, bool autoFormat, char indentChar, unsigned indentSize, unsigned level ) const override;

private:

   String m_name;
   String m_parameters;
};

// ----------------------------------------------------------------------------

/*!
 * \class XMLDeclaration
 * \brief %XML declaration
 *
 * %XMLDeclaration represents an %XML declaration in an %XML document prolog:
 *
 * https://www.w3.org/TR/xml11/#sec-prolog-dtd
 *
 * \ingroup xml_parsing_and_generation
 */
class PCL_CLASS XMLDeclaration : public XMLComponent
{
public:

   /*!
    * Constructs a new %XMLDeclaration object with the specified \a version,
    * optional \a encoding and \a standalone document specification.
    */
   XMLDeclaration( const String& version = String(), const String& encoding = String(), bool standalone = false )
      : m_version( version )
      , m_encoding( encoding )
      , m_standalone( standalone )
   {
   }

   /*!
    * Copy constructor.
    */
   XMLDeclaration( const XMLDeclaration& ) = default;

   /*!
    * Returns a reference to the (immutable) %XML version string.
    */
   const String& Version() const
   {
      return m_version;
   }

   /*!
    * Returns a reference to the (immutable) document encoding string.
    */
   const String& DocumentEncoding() const
   {
      return m_encoding;
   }

   /*!
    * Returns true iff this %XML declaration specifies a standalone document.
    */
   bool IsStandaloneDocument() const
   {
      return m_standalone;
   }

   /*!
    * Returns true iff this %XML declaration has been defined. This function
    * can be used to check if a parsed %XML document includes an %XML
    * declaration.
    */
   bool IsDefined() const
   {
      return !m_version.IsEmpty();
   }

   /*!
    * Serializes this %XML declaration.
    *
    * The generated serialization will be appended to the specified 8-bit
    * \a text string. If no version string has been defined for this object, a
    * 'version="1.0"' attribute will be generated. Similarly, if no encoding
    * string has been defined, an 'encoding="UTF-8"' attribute will be
    * generated.
    */
   void Serialize( IsoString& text ) const;

private:

   String m_version;
   String m_encoding;
   bool   m_standalone = false;
};

// ----------------------------------------------------------------------------

/*!
 * \class XMLDocTypeDeclaration
 * \brief %XML DOCTYPE declaration
 *
 * %XMLDocTypeDeclaration represents a document type declaration in an %XML
 * document prolog:
 *
 * https://www.w3.org/TR/xml11/#dt-doctype
 *
 * \ingroup xml_parsing_and_generation
 */
class PCL_CLASS XMLDocTypeDeclaration : public XMLComponent
{
public:

   /*!
    * Constructs a new %XMLDocTypeDeclaration object with the specified
    * document type \a name and type \a definition.
    */
   XMLDocTypeDeclaration( const String& name = String(), const String& definition = String() )
      : m_name( name )
      , m_definition( definition )
   {
   }

   /*!
    * Copy constructor.
    */
   XMLDocTypeDeclaration( const XMLDocTypeDeclaration& ) = default;

   /*!
    * Returns a reference to the (immutable) document type name.
    */
   const String& Name() const
   {
      return m_name;
   }

   /*!
    * Returns a reference to the (immutable) document type definition.
    */
   const String& Definition() const
   {
      return m_definition;
   }

   /*!
    * Returns true iff this document type declaration has been defined. This
    * function can be used to check if a parsed %XML document includes an
    * DOCTYPE declaration.
    */
   bool IsDefined() const
   {
      return !m_name.IsEmpty();
   }

   /*!
    * Serializes this DOCTYPE declaration with UTF-8 encoding.
    *
    * The generated serialization will be appended to the specified 8-bit
    * \a text string, encoded in UTF-8. This function won't generate any
    * characters if no document type name has been defined for this object.
    */
   void Serialize( IsoString& text ) const;

private:

   String m_name;
   String m_definition;
};

// ----------------------------------------------------------------------------

/*!
 * \class XMLElementFilter
 * \brief A functional class for filtering %XML elements
 *
 * Element filters can be used with XMLDocument objects to reject elements
 * selectively while parsing an %XML document.
 *
 * When an element filter has been defined, %XMLDocument calls its
 * reimplemented operator()( const XMLElement*, const String& ) member function
 * when an element's start-tag is found, passing its parent element and name to
 * the function. If the function returns false, the element is rejected,
 * including all of its child nodes, and the parsing process continues after
 * the corresponding end-tag.
 *
 * If the first function call described above returns true, %XMLDocument calls
 * operator()( const XMLElement*, const String&, const XMLAttributeList& ) with
 * the parent, name and attributes of the element. Again, if the function
 * returns false the element is skipped completely until its end-tag is found.
 * If the function returns true, the element is accepted, added to the DOM
 * being generated, and parsed.
 *
 * Element filters can be useful to accelerate %XML document parsing and reduce
 * its memory consumption considerably, when only a subset of possible elements
 * is required.
 *
 * \ingroup xml_parsing_and_generation
 */
struct XMLElementFilter
{
   /*!
    * Virtual destructor.
    */
   virtual ~XMLElementFilter()
   {
   }

   /*!
    * Returns true if an %XML element with the specified \a name and \a parent
    * element is acceptable; false if the element must be ignored.
    */
   virtual bool operator()( const XMLElement* parent, const String& name ) const = 0;

   /*!
    * Returns true if an %XML element with the specified \a name, \a attributes
    * and \a parent element is acceptable; false if the element must be
    * ignored. This function is only called after a previous call to
    * operator()( const XMLElement*, const String& ) has returned true for the
    * same element.
    *
    * The default implementation returns true, which facilitates
    * implementations where elements are only filtered by their names.
    */
   virtual bool operator()( const XMLElement* parent, const String& name, const XMLAttributeList& attributes ) const
   {
      return true;
   }
};

// ----------------------------------------------------------------------------

/*!
 * \namespace pcl::XMLParserOption
 * \brief     %XML document parsing options
 *
 * <table border="1" cellpadding="4" cellspacing="0">
 * <tr><td>XMLParserOption::IgnoreComments</td>           <td>Do not add comment nodes to the DOM.</td></tr>
 * <tr><td>XMLParserOption::IgnoreUnknownElements</td>    <td>Do not add unknown/invalid elements to the DOM.</td></tr>
 * <tr><td>XMLParserOption::IgnoreStrayCharacters</td>    <td>Be tolerant of non-space characters outside markup.</td></tr>
 * <tr><td>XMLParserOption::NormalizeTextSpaces</td>      <td>Trim and collapse spaces in all child text nodes.</td></tr>
 * </table>
 *
 * \ingroup xml_parsing_and_generation
 */
namespace XMLParserOption
{
   enum mask_type
   {
      IgnoreComments        = 0x00000001,
      IgnoreUnknownElements = 0x00000002,
      IgnoreStrayCharacters = 0x00000004,
      NormalizeTextSpaces   = 0x00000008
   };
}

/*!
 * \class pcl::XMLParserOptions
 * \brief A collection of %XML document parsing options
 * \ingroup xml_parsing_and_generation
 */
typedef Flags<XMLParserOption::mask_type>  XMLParserOptions;

// ----------------------------------------------------------------------------

/*!
 * \class XMLDocument
 * \brief %XML document parsing and generation
 *
 * %XMLDocument implements parsing and generation of well-formed %XML
 * documents.
 *
 * The Parse() member function reads and interprets a Unicode text string to
 * generate a read-only document object model (DOM) that represents the data
 * entities defined by a well-formed %XML document. The DOM can be inspected
 * with several member functions of the %XMLDocument class. All %XML nodes and
 * elements in a document can be visited recursively with specialized accessor
 * functions and iterators. See the Begin() and End() functions (and their
 * STL-compatible equivalents, begin() and end()), as well as XML(), DocType(),
 * RootElement(), and operator []( int ), among others.
 *
 * For generation of %XML documents, the Serialize() member function builds a
 * new document as a Unicode string encoded in UTF-8. The document's root node
 * and several nodes and critical components must be defined before document
 * generation - see the SetXML(), SetDocType(), AddNode() and SetRootElement()
 * member functions.
 *
 * For general information on %XML, the authoritative sources are the W3C
 * recommendations:
 *
 * https://www.w3.org/TR/xml/
 * https://www.w3.org/TR/xml11/
 * https://www.w3.org/TR/xml-names/
 *
 * The following example shows how an existing document can be parsed as a new
 * %XMLDocument object, and then a new %XML document can be generated and
 * written to a disk file, all in just three source code lines:
 *
 * \code
 * XMLDocument xml;
 * xml.Parse( File::ReadTextFile( "/path/to/file.xml" ).UTF8ToUTF16() );
 * File::WriteTextFile( "/tmp/test.xml", xml.Serialize() );
 * \endcode
 *
 * In this case the new document is generated without superfluous space
 * characters. To enable automatic indentation of text lines, see the
 * EnableAutoFormatting(), SetIndentSize() and EnableIndentTabs() member
 * functions.
 *
 * The following example:
 *
 * \code
 * XMLElement* e1 = new XMLElement( "Foo", XMLAttributeList() << XMLAttribute( "version", "1.0" ) );
 *
 * XMLElement* e2 = new XMLElement( "Bar" );
 * *e2 << new XMLElement( "bar_child_1" )
 *     << new XMLElement( "bar_child_2" );
 *
 * XMLElement* e3 = new XMLElement( "FooBar" );
 * *e3 << new XMLText( "This is FooBar." );
 *
 * *e1 << e2 << e3;
 *
 * XMLDocument xml;
 * xml.SetXML( "1.0" );
 * xml.SetRootElement( e1 );
 * xml.EnableAutoFormatting();
 * xml.SerializeToFile( "/tmp/foobar.xml" );
 * \endcode
 *
 * generates this %XML file in /tmp/foobar.xml:
 *
 * \code
 * <?xml version="1.0" encoding="UTF-8"?>
 * <Foo version="1.0">
 *    <Bar>
 *       <bar_child_1/>
 *       <bar_child_2/>
 *    </Bar>
 *    <FooBar>This is FooBar.</FooBar>
 * </Foo>
 * \endcode
 *
 * \ingroup xml_parsing_and_generation
 */
class PCL_CLASS XMLDocument
{
public:

   /*!
    * Represents a mutable child node list iterator.
    */
   typedef XMLNodeList::iterator          iterator;

   /*!
    * Represents an immutable child node list iterator.
    */
   typedef XMLNodeList::const_iterator    const_iterator;

   /*!
    * Represents an option to control the %XML parser behavior. Valid options
    * are defined in the XMLParserOption namespace.
    */
   typedef XMLParserOption::mask_type     parser_option;

   /*!
    * Default constructor. Constructs an empty %XML document.
    *
    * For serialization of XML documents, this constructor defines the
    * following default settings:
    *
    * \li Auto-formatting disabled.
    * \li Use space characters (\#x20) for indentation.
    * \li Indentation size = 3 spaces.
    */
   XMLDocument() = default;

   /*!
    * Virtual destructor. Recursively destroys all %XML elements, declarations
    * and auxiliary data associated with this object.
    */
   virtual ~XMLDocument()
   {
      m_nodes.Destroy();
      m_root = nullptr;
      RemoveElementFilter();
   }

   /*!
    * Copy constructor. This constructor is disabled because %XMLDocument
    * represents unique objects.
    */
   XMLDocument( const XMLDocument& ) = delete;

   /*!
    * Copy assignment. This operator is disabled because %XMLDocument
    * represents unique objects.
    */
   XMLDocument& operator =( const XMLDocument& ) = delete;

   /*!
    * Returns a reference to the (immutable) %XML declaration object associated
    * with this document.
    */
   const XMLDeclaration& XML() const
   {
      return m_xml;
   }

   /*!
    * Defines an %XML declaration in this %XML document.
    */
   void SetXML( const XMLDeclaration& xml )
   {
      m_xml = xml;
   }

   /*!
    * Defines an %XML declaration in this %XML document with the specified
    * \a version, \a encoding and \a standalone attributes.
    */
   void SetXML( const String& version = "1.0", const String& encoding = "UTF-8", bool standalone = false )
   {
      SetXML( XMLDeclaration( version, encoding, standalone ) );
   }

   /*!
    * Returns a reference to the (immutable) %XML document type declaration
    * object associated with this document.
    */
   const XMLDocTypeDeclaration& DocType() const
   {
      return m_docType;
   }

   /*!
    * Associates a new %XML document type declaration object with this %XML
    * document.
    */
   void SetDocType( const XMLDocTypeDeclaration& docType )
   {
      m_docType = docType;
   }

   /*!
    * Returns a pointer to the (immutable) root element of this %XML document.
    * If there is no root element, for example when this is an uninitialized
    * %XMLDocument instance, this function returns \c nullptr.
    *
    * \sa ReleaseRootElement(), SetRootElement()
    */
   const XMLElement* RootElement() const
   {
      return m_root;
   }

   /*!
    * Releases the root element of this %XML document.
    *
    * This function returns the root element and causes this object to forget
    * it. The caller will be responsible for destroying and deallocating the
    * returned XMLElement instance as appropriate. This function performs an
    * implicit call to Clear(), so the document will be empty after calling it.
    *
    * If there is no root element, for example when this is an uninitialized
    * %XMLDocument instance, this function returns \c nullptr.
    *
    * \sa RootElement(), SetRootElement()
    */
   XMLElement* ReleaseRootElement()
   {
      XMLElement* root = m_root;
      m_nodes.RemovePointer( m_root );
      Clear();
      return root;
   }

   /*!
    * Returns the number of nodes in this %XML document, or zero if this is an
    * empty or uninitialized %XMLDocument object.
    */
   int NodeCount() const
   {
      return int( m_nodes.Length() );
   }

   /*!
    * Returns true iff this is an empty %XML document. An empty document has no
    * %XML nodes.
    */
   bool IsEmpty() const
   {
      return m_nodes.IsEmpty();
   }

   /*!
    * Returns a reference to the (immutable) document node at the specified
    * zero-based index \a i. No bounds checking is performed: if the specified
    * index is invalid, this function invokes undefined behavior.
    */
   const XMLNode& operator []( int i ) const
   {
      return m_nodes[i];
   }

   /*!
    * Returns an immutable iterator located at the beginning of the list of
    * nodes of this %XML document.
    */
   const_iterator Begin() const
   {
      return m_nodes.Begin();
   }

   /*!
    * Returns an immutable iterator located at the end of the list of
    * nodes of this %XML document.
    */
   const_iterator End() const
   {
      return m_nodes.End();
   }

#ifndef __PCL_NO_STL_COMPATIBLE_ITERATORS
   /*!
    * STL-compatible iteration. Equivalent to Begin() const.
    */
   const_iterator begin() const
   {
      return Begin();
   }

   /*!
    * STL-compatible iteration. Equivalent to End() const.
    */
   const_iterator end() const
   {
      return End();
   }
#endif   // !__PCL_NO_STL_COMPATIBLE_ITERATORS

   /*!
    * Appends a new top-level %XML node to this document.
    *
    * If the specified \a node already belongs to an %XMLDocument object, or if
    * a null pointer is specified, this member function will throw an Error
    * exception.
    *
    * The specified \a node will be appended to the current list of document
    * nodes. If there is a root element in this document, the new \a node will
    * be appended after the root element.
    *
    * The \a node will be owned by this document object, which will destroy and
    * deallocate it automatically when appropriate.
    */
   void AddNode( XMLNode* node );

   /*!
    * Insertion operator. Returns a reference to this %XMLDocument object.
    *
    * This operator is equivalent to AddNode( XMLNode* ).
    */
   XMLDocument& operator <<( XMLNode* node )
   {
      AddNode( node );
      return *this;
   }

   /*!
    * Sets the root element of this %XML document.
    *
    * If the specified \a element already belongs to an %XMLDocument object, if
    * a null pointer is specified, or if a root node has already been defined
    * for this document, this member function will throw an Error exception.
    *
    * The specified \a element will be appended to the current list of document
    * nodes. The \a element will be owned by this document object, which will
    * destroy and deallocate it automatically when appropriate.
    *
    * \sa RootElement(), ReleaseRootElement()
    */
   void SetRootElement( XMLElement* element );

   /*!
    * Destroys and deallocates all nodes and elements in this %XML document
    * object, and initializes all internal structures to a default state,
    * yielding an uninitialized object.
    *
    * If there is an element filter or a set of parser options defined for this
    * object, they are preserved by this function. See RemoveElementFilter() to
    * remove a filter set by a previous call to SetElementFilter(). See also
    * ClearParserOptions() to reset parser options set by previous calls to
    * SetParserOption().
    */
   void Clear();

   /*!
    * Sets a new element filter for this object. The specified object will be
    * owned by this %XMLDocument instance, which will destroy and deallocate it
    * when appropriate.
    *
    * See XMLElementFilter for a complete description of the element filtering
    * functionality. See RemoveElementFilter() to remove the element filter
    * set by this function.
    */
   void SetElementFilter( XMLElementFilter* filter )
   {
      delete m_filter, m_filter = filter;
   }

   /*!
    * Removes an element filter set by a previous call to SetElementFilter().
    * If no filter has been defined for this object, this function has no
    * effect.
    */
   void RemoveElementFilter()
   {
      SetElementFilter( nullptr );
   }

   /*!
    * Enables or disables an %XML document parser option for this object. Valid
    * options are defined in the XMLParserOption namespace. See
    * ClearParserOptions() to reset all parser options to a default state.
    */
   void SetParserOption( parser_option option, bool on = true )
   {
      m_parserOptions.SetFlag( option, on );
   }

   /*!
    * Sets the specified parser \a options. Valid options are defined in the
    * XMLParserOption namespace. See ClearParserOptions() to reset all parser
    * options to a default state.
    */
   void SetParserOptions( XMLParserOptions options )
   {
      m_parserOptions = options;
   }

   /*!
    * Resets all parser options defined for this object by a previous call to
    * SetParserOption() or SetParserOptions().
    */
   void ClearParserOptions()
   {
      m_parserOptions.Clear();
   }

   /*!
    * %XML document parser. Reads and interprets the specified Unicode \a text
    * string, which must be encoded in UTF-16, as a well-formed %XML document.
    *
    * This member function generates a document object model (DOM) to represent
    * the data entities defined by the source %XML document. The DOM can then
    * be inspected with several member functions of the %XMLDocument class. All
    * %XML nodes and elements can be visited recursively with specialized
    * iterators. See the Begin() and End() functions (and their STL-compatible
    * equivalents, begin() and end()), as well as XML(), DocType(),
    * RootElement() and operator []( int ), among others.
    */
   void Parse( const String& text );

   /*!
    * Returns true iff the auto-formatting feature is enabled for %XML
    * serialization with this %XMLDocument object.
    *
    * When auto-formatting is enabled, ignorable line breaks (\#x0A) and white
    * space characters (either spaces (\#x20) or tabulators (\#x09)) are used
    * to separate %XML nodes and to indent text lines, respectively, improving
    * readability of generated %XML code. When auto-formatting is disabled, no
    * superfluous white space characters are generated. The only exception is
    * XMLText child nodes with space preservation enabled, which always ignore
    * all indentation and formatting settings in order to reproduce their text
    * contents without modification.
    *
    * The auto-formatting feature is always disabled by default for newly
    * constructed %XMLDocument objects. This is because the main purpose and
    * utility of %XMLDocument is parsing and generation of %XML documents
    * intended for automated data management, without direct user intervention.
    * Auto-formatting is only useful for human readability of %XML source code.
    */
   bool IsAutoFormatting() const
   {
      return m_autoFormatting;
   }

   /*!
    * Enables the auto-formatting feature for generation of %XML code. See
    * IsAutoFormatting() for more information.
    */
   void EnableAutoFormatting( bool enable = true )
   {
      m_autoFormatting = enable;
   }

   /*!
    * Disables the auto-formatting feature for generation of %XML code. See
    * IsAutoFormatting() for more information.
    */
   void DisableAutoFormatting( bool disable = true )
   {
      EnableAutoFormatting( !disable );
   }

   /*!
    * Returns the number of space characters (\#x20) used for each indentation
    * level of text lines, when the auto-formatting feature is enabled and
    * space characters are used for indentation.
    *
    * When tabulator characters (\#x09) are used for indentation, this setting
    * is ignored and a single tabulator is always used for each indentation
    * level. See IsAutoFormatting() and SetIndentSize() for more information.
    */
   int IndentSize() const
   {
      return m_indentSize;
   }

   /*!
    * Sets the number of indentation space characters.
    *
    * \param indentSize    Number of space characters (\#x20) used for a level
    *                      of indentation of text lines in generated %XML code,
    *                      when the auto-formatting feature is enabled and
    *                      space characters are used for indentation. The valid
    *                      range of values is from zero (for no indentation) to
    *                      8 characters.
    *
    * When the indentation size is zero and auto-formatting is enabled, each
    * document node is generated in a separate line without any indentation.
    * XMLText child nodes with space preservation enabled will always ignore
    * all indentation and formatting settings, in order to reproduce their text
    * contents without modification.
    *
    * When tabulator characters (\#x09) are used for indentation, this setting
    * is ignored and a single tabulator character is always used for each
    * indentation level.
    *
    * The default indentation size is 3 for newly constructed %XMLDocument
    * objects.
    */
   void SetIndentSize( int indentSize )
   {
      m_indentSize = Range( indentSize, 0, 8 );
   }

   /*!
    * Returns true if tabulator characters (\#x09) are used for indentation of
    * text lines, when the auto-formatting feature is enabled. Returns false if
    * space characters (\#x20) are used for indentation.
    *
    * By default, text indentation is always performed using space characters
    * by newly constructed %XMLDocument objects.
    */
   bool IsIndentTabs() const
   {
      return m_indentTabs;
   }

   /*!
    * Enables the use of tabulator characters (\#x09) for indentation. See
    * IsIndentTabs() for more information.
    */
   void EnableIndentTabs( bool enable = true )
   {
      m_indentTabs = enable;
   }

   /*!
    * Disables the use of tabulator characters (\#x09) for indentation. See
    * IsIndentTabs() for more information.
    */
   void DisableIndentTabs( bool disable = true )
   {
      EnableIndentTabs( !disable );
   }

   /*!
    * Serializes this %XML document. Returns the generated serialization as a
    * Unicode string encoded in UTF-8.
    *
    * To serialize a well-formed %XML document, this object must be initialized
    * first by defining a root element (see SetRootElement()) and other
    * document nodes, as necessary (see SetXML(), SetDocType(), and AddNode()).
    *
    * For formatting and indentation settings, see IsAutoFormatting(),
    * IndentSize() and IsIndentTabs().
    */
   IsoString Serialize() const;

   /*!
    * Serializes this %XML document and writes the result to a file at the
    * specified \a path with UTF-8 encoding.
    *
    * See Serialize() for more information.
    *
    * \warning If a file already exists at the specified path, its previous
    * contents will be lost after calling this function.
    */
   void SerializeToFile( const String& path ) const;

private:

   XMLDeclaration        m_xml;
   XMLDocTypeDeclaration m_docType;
   XMLNodeList           m_nodes;
   XMLElement*           m_root = nullptr;
   XMLElementFilter*     m_filter = nullptr;
   XMLParserOptions      m_parserOptions;
   XMLNodeLocation       m_location;
   bool                  m_autoFormatting = false;
   bool                  m_indentTabs = false;
   int                   m_indentSize = 3;
};

// ----------------------------------------------------------------------------

} // pcl

#endif   // __PCL_XML_h

// ----------------------------------------------------------------------------
// EOF pcl/XML.h - Released 2022-03-12T18:59:29Z
