text: /*
   * Copyright (c) Contributors to the Open 3D Engine Project.
   * For complete copyright and license terms please see the LICENSE at the root of this distribution.
   *
   * SPDX-License-Identifier: Apache-2.0 OR MIT
   *
   */
  
  #pragma once
  
  
  #include <algorithm>
  #include <stack>
  
  #include "IXml.h"
  
  // track some XML stats. only to find persistent XML nodes in the system
  // slow, so disable by default
  //#define CRY_COLLECT_XML_NODE_STATS
  //#undef CRY_COLLECT_XML_NODE_STATS
  
  
  struct IXmlStringPool
  {
  public:
      IXmlStringPool() { m_refCount = 0; }
      virtual ~IXmlStringPool() {};
      void AddRef() { m_refCount++; };
      void Release()
      {
          if (--m_refCount <= 0)
          {
              delete this;
          }
      };
      virtual const char* AddString(const char* str) = 0;
  private:
      int m_refCount;
  };
  
  /************************************************************************/
  /* XmlParser class, Parse xml and return root xml node if success.      */
  /************************************************************************/
  class XmlParser
      : public IXmlParser
  {
  public:
      explicit XmlParser(bool bReuseStrings);
      ~XmlParser();
  
      void AddRef()
      {
          ++m_nRefCount;
      }
  
      void Release()
      {
          if (--m_nRefCount <= 0)
          {
              delete this;
          }
      }
  
      virtual XmlNodeRef ParseFile(const char* filename, bool bCleanPools);
  
      virtual XmlNodeRef ParseBuffer(const char* buffer, int nBufLen, bool bCleanPools, bool bSuppressWarnings = false);
  
      const char* getErrorString() const { return m_errorString; }
  
  private:
      int m_nRefCount;
      XmlString m_errorString;
      class XmlParserImp* m_pImpl;
  };
  
  // Compare function for string comparasion, can be strcmp or _stricmp
  typedef int (__cdecl * XmlStrCmpFunc)(const char* str1, const char* str2);
  extern XmlStrCmpFunc g_pXmlStrCmp;
  
  //////////////////////////////////////////////////////////////////////////
  // XmlAttribute class
  //////////////////////////////////////////////////////////////////////////
  struct XmlAttribute
  {
      const char* key;
      const char* value;
  
      bool operator<(const XmlAttribute& attr) const { return g_pXmlStrCmp(key, attr.key) < 0; }
      bool operator>(const XmlAttribute& attr) const { return g_pXmlStrCmp(key, attr.key) > 0; }
      bool operator==(const XmlAttribute& attr) const { return g_pXmlStrCmp(key, attr.key) == 0; }
      bool operator!=(const XmlAttribute& attr) const { return g_pXmlStrCmp(key, attr.key) != 0; }
  };
  
  //! Xml node attributes class.
  
  typedef std::vector<XmlAttribute>   XmlAttributes;
  typedef XmlAttributes::iterator XmlAttrIter;
  typedef XmlAttributes::const_iterator XmlAttrConstIter;
  
  /**
  ******************************************************************************
  * CXmlNode class
  * Never use CXmlNode directly instead use reference counted XmlNodeRef.
  ******************************************************************************
  */
  
  class CXmlNode
      : public IXmlNode
  {
  public:
      //! Constructor.
      CXmlNode();
      CXmlNode(const char* tag, bool bReuseStrings, bool bIsProcessingInstruction = false);
      //! Destructor.
      ~CXmlNode();
  
      //////////////////////////////////////////////////////////////////////////
      // Custom new/delete with pool allocator.
      //////////////////////////////////////////////////////////////////////////
      //void* operator new( size_t nSize );
      //void operator delete( void *ptr );
  
      virtual void DeleteThis();
  
      //! Create new XML node.
      XmlNodeRef createNode(const char* tag);
  
      //! Get XML node tag.
      const char* getTag() const { return m_tag; };
      void    setTag(const char* tag);
  
      //! Return true if given tag equal to node tag.
      bool isTag(const char* tag) const;
  
      //! Get XML Node attributes.
      virtual int getNumAttributes() const { return m_pAttributes ? (int)m_pAttributes->size() : 0; };
      //! Return attribute key and value by attribute index.
      virtual bool getAttributeByIndex(int index, const char** key, const char** value);
  
      //! Return attribute key and value by attribute index, string version.
      virtual bool getAttributeByIndex(int index, XmlString& key, XmlString& value);
  
  
      virtual void copyAttributes(XmlNodeRef fromNode);
      virtual void shareChildren(const XmlNodeRef& fromNode);
  
      //! Get XML Node attribute for specified key.
      const char* getAttr(const char* key) const;
  
      //! Get XML Node attribute for specified key.
      // Returns true if the attribute existes, alse otherwise.
      bool                getAttr(const char* key, const char** value) const;
  
      //! Check if attributes with specified key exist.
      bool haveAttr(const char* key) const;
  
      //! Creates new xml node and add it to childs list.
      XmlNodeRef newChild(const char* tagName);
  
      //! Adds new child node.
      void addChild(const XmlNodeRef& node);
      //! Remove child node.
      void removeChild(const XmlNodeRef& node);
  
      void insertChild(int nIndex, const XmlNodeRef& node);
      void replaceChild(int nIndex, const XmlNodeRef& node);
  
      //! Remove all child nodes.
      void removeAllChilds();
  
      //! Get number of child XML nodes.
      int getChildCount() const { return m_pChilds ? (int)m_pChilds->size() : 0; };
  
      //! Get XML Node child nodes.
      XmlNodeRef getChild(int i) const;
  
      //! Find node with specified tag.
      XmlNodeRef findChild(const char* tag) const;
      void deleteChild(const char* tag);
      void deleteChildAt(int nIndex);
  
      //! Get parent XML node.
      XmlNodeRef  getParent() const { return m_parent; }
      void                setParent(const XmlNodeRef& inRef);
  
  
      //! Returns content of this node.
      const char* getContent() const { return m_content; };
      void setContent(const char* str);
  
      XmlNodeRef  clone();
  
      //! Returns line number for XML tag.
      int getLine() const { return m_line; };
      //! Set line number in xml.
      void setLine(int line) { m_line = line; };
  
      //! Returns XML of this node and sub nodes.
      virtual IXmlStringData* getXMLData(int nReserveMem = 0) const;
      XmlString getXML(int level = 0) const;
      XmlString getXMLUnsafe(int level, char* tmpBuffer, uint32 sizeOfTmpBuffer) const;
      bool saveToFile(const char* fileName);   // saves in one huge chunk
      bool saveToFile(const char* fileName, size_t chunkSizeBytes, AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle); // save in small memory chunks
  
      //! Set new XML Node attribute (or override attribute with same key).
      using IXmlNode::setAttr;
      void setAttr(const char* key, const char* value);
      void setAttr(const char* key, int value);
      void setAttr(const char* key, unsigned int value);
      void setAttr(const char* key, int64 value);
      void setAttr(const char* key, uint64 value, bool useHexFormat = true);
      void setAttr(const char* key, float value);
      void setAttr(const char* key, double value);
      void setAttr(const char* key, const Vec2& value);
      void setAttr(const char* key, const Ang3& value);
      void setAttr(const char* key, const Vec3& value);
      void setAttr(const char* key, const Vec4& value);
      void setAttr(const char* key, const Quat& value);
  
      //! Delete attrbute.
      void delAttr(const char* key);
      //! Remove all node attributes.
      void removeAllAttributes();
  
      //! Get attribute value of node.
      bool getAttr(const char* key, int& value) const;
      bool getAttr(const char* key, unsigned int& value) const;
      bool getAttr(const char* key, int64& value) const;
      bool getAttr(const char* key, uint64& value, bool useHexFormat = true  /*ignored*/) const;
      bool getAttr(const char* key, float& value) const;
      bool getAttr(const char* key, double& value) const;
      bool getAttr(const char* key, bool& value) const;
  
      bool getAttr(const char* key, XmlString& value) const  {const char*    v(NULL); bool  boHasAttribute(getAttr(key, &v)); value = v; return boHasAttribute; }
  
      bool getAttr(const char* key, Vec2& value) const;
      bool getAttr(const char* key, Ang3& value) const;
      bool getAttr(const char* key, Vec3& value) const;
      bool getAttr(const char* key, Vec4& value) const;
      bool getAttr(const char* key, Quat& value) const;
      bool getAttr(const char* key, ColorB& value) const;
  
  protected:
  
  private:
      CXmlNode(const CXmlNode&);
      CXmlNode& operator = (const CXmlNode&);
  
  private:
      void ReleaseChild(IXmlNode* pChild);
      void removeAllChildsImpl();
  
      void AddToXmlString(XmlString& xml, int level, AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle, size_t chunkSizeBytes = 0) const;
      char* AddToXmlStringUnsafe(char* xml, int level, char* endPtr, AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle, size_t chunkSizeBytes = 0) const;
      XmlString MakeValidXmlString(const XmlString& xml) const;
      bool IsValidXmlString(const char* str) const;
      XmlAttrConstIter GetAttrConstIterator(const char* key) const
      {
          assert(m_pAttributes);
  
          XmlAttribute tempAttr;
          tempAttr.key = key;
  
          XmlAttributes::const_iterator it = std::find(m_pAttributes->begin(), m_pAttributes->end(), tempAttr);
          return it;
  
          /*
          XmlAttributes::const_iterator it = std::lower_bound( m_attributes.begin(),m_attributes.end(),tempAttr );
          if (it != m_attributes.end() && _stricmp(it->key,key) == 0)
          return it;
          return m_attributes.end();
          */
      }
      XmlAttrIter GetAttrIterator(const char* key)
      {
          assert(m_pAttributes);
  
          XmlAttribute tempAttr;
          tempAttr.key = key;
  
          XmlAttributes::iterator it = std::find(m_pAttributes->begin(), m_pAttributes->end(), tempAttr);
          return it;
  
          //      XmlAttributes::iterator it = std::lower_bound( m_attributes.begin(),m_attributes.end(),tempAttr );
          //if (it != m_attributes.end() && _stricmp(it->key,key) == 0)
          //return it;
          //return m_attributes.end();
      }
      const char* GetValue(const char* key) const
      {
          if (m_pAttributes)
          {
              XmlAttrConstIter it = GetAttrConstIterator(key);
              if (it != m_pAttributes->end())
              {
                  return it->value;
              }
          }
          return 0;
      }
  
  protected:
      // String pool used by this node.
      IXmlStringPool* m_pStringPool;
  
      //! Tag of XML node.
      const char* m_tag;
  
  private:
  
      //! Content of XML node.
      const char* m_content;
      //! Parent XML node.
      IXmlNode* m_parent;
  
      //typedef DynArray<CXmlNode*,XmlDynArrayAlloc>  XmlNodes;
      typedef std::vector<IXmlNode*>  XmlNodes;
      //XmlNodes m_childs;
      XmlNodes* m_pChilds;
  
      //! Xml node attributes.
      //XmlAttributes m_attributes;
      XmlAttributes* m_pAttributes;
  
      //! Line in XML file where this node firstly appeared (useful for debugging).
      int m_line;
  
      bool m_isProcessingInstruction;
      friend class XmlParserImp;
  };
  
  #ifdef CRY_COLLECT_XML_NODE_STATS
  typedef std::set<CXmlNode*> TXmlNodeSet; // yes, slow, but really only for one-shot debugging
  struct SXmlNodeStats
  {
      SXmlNodeStats()
          : nAllocs(0)
          , nFrees(0) {}
      TXmlNodeSet nodeSet;
      uint32 nAllocs;
      uint32 nFrees;
  };
  extern SXmlNodeStats* g_pCXmlNode_Stats;
  #endif
  
  
  //////////////////////////////////////////////////////////////////////////
  //
  // Reusable XmlNode for XmlNode pool with shared xml string pool
  //
  //////////////////////////////////////////////////////////////////////////
  class CXmlNodePool;
  
  class CXmlNodeReuse
      : public CXmlNode
  {
  public:
      CXmlNodeReuse(const char* tag, CXmlNodePool* pPool);
      virtual void Release();
  
  protected:
      CXmlNodePool* m_pPool;
  };
  
  //////////////////////////////////////////////////////////////////////////
  //
  // Pool of reusable XML nodes with shared string pool
  //
  //////////////////////////////////////////////////////////////////////////
  class CXmlNodePool
  {
  public:
      CXmlNodePool(unsigned int nBlockSize, bool bReuseStrings);
      virtual ~CXmlNodePool();
  
      XmlNodeRef GetXmlNode(const char* sNodeName);
      bool empty() const { return (m_nAllocated == 0); }
  
  protected:
      virtual void OnRelease(int iRefCount, void* pThis);
      IXmlStringPool* GetStringPool() { return m_pStringPool; }
  
  private:
      friend class CXmlNodeReuse;
  
      IXmlStringPool* m_pStringPool;
      unsigned int m_nAllocated;
      std::stack<CXmlNodeReuse*> m_pNodePool;
  };
  