From 5abd66dfa3101899f583a6ff3471ec97359269a2 Mon Sep 17 00:00:00 2001 From: Lukas Aldershaab Date: Mon, 3 Jan 2022 21:10:26 +0100 Subject: [PATCH] Split PrettyPrinting functionality from VFS printing --- Engine/source/persistence/taml/fsTinyXml.cpp | 254 ++++++++----------- Engine/source/persistence/taml/fsTinyXml.h | 107 +++++++- 2 files changed, 212 insertions(+), 149 deletions(-) diff --git a/Engine/source/persistence/taml/fsTinyXml.cpp b/Engine/source/persistence/taml/fsTinyXml.cpp index 09da8d16b..32eb5de38 100644 --- a/Engine/source/persistence/taml/fsTinyXml.cpp +++ b/Engine/source/persistence/taml/fsTinyXml.cpp @@ -26,49 +26,10 @@ #include "console/console.h" - -// Re-implement private functionality in TinyXML2 - -static const char LINE_FEED = static_cast(0x0a); // all line endings are normalized to LF -static const char LF = LINE_FEED; -static const char CARRIAGE_RETURN = static_cast(0x0d); // CR gets filtered out -static const char CR = CARRIAGE_RETURN; -static const char SINGLE_QUOTE = '\''; -static const char DOUBLE_QUOTE = '\"'; - -struct Entity { - const char* pattern; - int length; - char value; -}; - -static const int NUM_ENTITIES = 5; -static const Entity entities[NUM_ENTITIES] = { - { "quot", 4, DOUBLE_QUOTE }, - { "amp", 3, '&' }, - { "apos", 4, SINGLE_QUOTE }, - { "lt", 2, '<' }, - { "gt", 2, '>' } -}; - VfsXMLPrinter::VfsXMLPrinter(FileStream& stream, bool compact, int depth) : XMLPrinter(NULL, compact, depth), - m_Stream(stream), - _depth(depth) + m_Stream(stream) { - for (int i = 0; i < ENTITY_RANGE; ++i) { - _entityFlag[i] = false; - _restrictedEntityFlag[i] = false; - } - for (int i = 0; i < NUM_ENTITIES; ++i) { - const char entityValue = entities[i].value; - const unsigned char flagIndex = static_cast(entityValue); - TIXMLASSERT(flagIndex < ENTITY_RANGE); - _entityFlag[flagIndex] = true; - } - _restrictedEntityFlag[static_cast('&')] = true; - _restrictedEntityFlag[static_cast('<')] = true; - _restrictedEntityFlag[static_cast('>')] = true; // not required, but consistency is nice } VfsXMLPrinter::~VfsXMLPrinter() @@ -77,72 +38,6 @@ VfsXMLPrinter::~VfsXMLPrinter() m_Stream.close(); } -void VfsXMLPrinter::PrintString(const char* p, bool restricted) -{ - // Look for runs of bytes between entities to print. - const char* q = p; - - if (_processEntities) { - const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag; - while (*q) { - TIXMLASSERT(p <= q); - // Remember, char is sometimes signed. (How many times has that bitten me?) - if (*q > 0 && *q < ENTITY_RANGE) { - // Check for entities. If one is found, flush - // the stream up until the entity, write the - // entity, and keep looking. - if (flag[static_cast(*q)]) { - while (p < q) { - const size_t delta = q - p; - const int toPrint = (INT_MAX < delta) ? INT_MAX : static_cast(delta); - Write(p, toPrint); - p += toPrint; - } - bool entityPatternPrinted = false; - for (int i = 0; i < NUM_ENTITIES; ++i) { - if (entities[i].value == *q) { - Putc('&'); - Write(entities[i].pattern, entities[i].length); - Putc(';'); - entityPatternPrinted = true; - break; - } - } - if (!entityPatternPrinted) { - // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release - TIXMLASSERT(false); - } - ++p; - } - } - ++q; - TIXMLASSERT(p <= q); - } - // Flush the remaining string. This will be the entire - // string if an entity wasn't found. - if (p < q) { - const size_t delta = q - p; - const int toPrint = (INT_MAX < delta) ? INT_MAX : static_cast(delta); - Write(p, toPrint); - } - } - else { - Write(p); - } -} - -bool VfsXMLPrinter::VisitEnter(const tinyxml2::XMLDocument& doc) -{ - _processEntities = doc.ProcessEntities(); - return XMLPrinter::VisitEnter(doc); -} - -bool VfsXMLPrinter::VisitExit(const tinyxml2::XMLElement& element) -{ - _depth--; - return XMLPrinter::VisitExit(element); -} - // Add VFS friendly implementations of output functions @@ -166,42 +61,6 @@ void VfsXMLPrinter::Putc(char ch) m_Stream.write(static_cast(ch)); } -// Overwrite Visitation of elements to add newlines before attributes - -bool VfsXMLPrinter::VisitEnter(const tinyxml2::XMLElement& element, const tinyxml2::XMLAttribute* attribute) -{ - const tinyxml2::XMLElement* parentElem = 0; - if (element.Parent()) { - parentElem = element.Parent()->ToElement(); - } - const bool compactMode = parentElem ? CompactMode(*parentElem) : CompactMode(element); - OpenElement(element.Name(), compactMode); - _depth++; - while (attribute) { - PushAttribute(attribute->Name(), attribute->Value(), compactMode); - attribute = attribute->Next(); - } - return true; -} - -void VfsXMLPrinter::PushAttribute(const char* name, const char* value, bool compactMode) -{ - TIXMLASSERT(_elementJustOpened); - if (compactMode) - { - Putc(' '); - } - else - { - Putc('\n'); - PrintSpace(_depth); - } - Write(name); - Write("=\""); - PrintString(value, false); - Putc('\"'); -} - bool VfsXMLDocument::LoadFile(const char* pFilename) { // Expand the file-path. @@ -340,7 +199,8 @@ bool VfsXMLDocument::SaveFile(FileStream& stream) // for *this* call. ClearError(); VfsXMLPrinter printer(stream, false, 0); - Print(&printer); + PrettyXMLPrinter prettyPrinter(printer); + Print(&prettyPrinter); return !Error(); } @@ -401,3 +261,111 @@ void VfsXMLDocument::SetError(tinyxml2::XMLError error, int lineNum, const char* _errorStr.SetStr(buffer); delete[] buffer; } + + +// Overwrite Visitation of elements to add newlines before attributes +PrettyXMLPrinter::PrettyXMLPrinter(VfsXMLPrinter& innerPrinter, int depth) + : mInnerPrinter(innerPrinter), + _depth(depth) +{ + for (int i = 0; i < ENTITY_RANGE; ++i) { + _entityFlag[i] = false; + _restrictedEntityFlag[i] = false; + } + for (int i = 0; i < NUM_ENTITIES; ++i) { + const char entityValue = entities[i].value; + const unsigned char flagIndex = static_cast(entityValue); + TIXMLASSERT(flagIndex < ENTITY_RANGE); + _entityFlag[flagIndex] = true; + } + _restrictedEntityFlag[static_cast('&')] = true; + _restrictedEntityFlag[static_cast('<')] = true; + _restrictedEntityFlag[static_cast('>')] = true; // not required, but consistency is nice +} + +void PrettyXMLPrinter::PrintString(const char* p, bool restricted) +{ + // Look for runs of bytes between entities to print. + const char* q = p; + + if (_processEntities) { + const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag; + while (*q) { + TIXMLASSERT(p <= q); + // Remember, char is sometimes signed. (How many times has that bitten me?) + if (*q > 0 && *q < ENTITY_RANGE) { + // Check for entities. If one is found, flush + // the stream up until the entity, write the + // entity, and keep looking. + if (flag[static_cast(*q)]) { + while (p < q) { + const size_t delta = q - p; + const int toPrint = (INT_MAX < delta) ? INT_MAX : static_cast(delta); + mInnerPrinter.Write(p, toPrint); + p += toPrint; + } + bool entityPatternPrinted = false; + for (int i = 0; i < NUM_ENTITIES; ++i) { + if (entities[i].value == *q) { + mInnerPrinter.Putc('&'); + mInnerPrinter.Write(entities[i].pattern, entities[i].length); + mInnerPrinter.Putc(';'); + entityPatternPrinted = true; + break; + } + } + if (!entityPatternPrinted) { + // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release + TIXMLASSERT(false); + } + ++p; + } + } + ++q; + TIXMLASSERT(p <= q); + } + // Flush the remaining string. This will be the entire + // string if an entity wasn't found. + if (p < q) { + const size_t delta = q - p; + const int toPrint = (INT_MAX < delta) ? INT_MAX : static_cast(delta); + mInnerPrinter.Write(p, toPrint); + } + } + else { + mInnerPrinter.Write(p); + } +} + +bool PrettyXMLPrinter::VisitEnter(const tinyxml2::XMLElement& element, const tinyxml2::XMLAttribute* attribute) +{ + const tinyxml2::XMLElement* parentElem = 0; + if (element.Parent()) { + parentElem = element.Parent()->ToElement(); + } + const bool compactMode = parentElem ? mInnerPrinter.CompactMode(*parentElem) : mInnerPrinter.CompactMode(element); + mInnerPrinter.OpenElement(element.Name(), compactMode); + _depth++; + while (attribute) { + PushAttribute(attribute->Name(), attribute->Value(), compactMode); + attribute = attribute->Next(); + } + return true; +} + +void PrettyXMLPrinter::PushAttribute(const char* name, const char* value, bool compactMode) +{ + if (compactMode) + { + mInnerPrinter.Putc(' '); + } + else + { + mInnerPrinter.Putc('\n'); + mInnerPrinter.PrintSpace(_depth); + } + mInnerPrinter.Write(name); + mInnerPrinter.Write("=\""); + PrintString(value, false); + mInnerPrinter.Putc('\"'); +} diff --git a/Engine/source/persistence/taml/fsTinyXml.h b/Engine/source/persistence/taml/fsTinyXml.h index 8c10463dc..972e05794 100644 --- a/Engine/source/persistence/taml/fsTinyXml.h +++ b/Engine/source/persistence/taml/fsTinyXml.h @@ -41,19 +41,16 @@ public: ~VfsXMLPrinter() override; // Re-implement private functionality in TinyXML2 library, this is just a copy-paste job - void PrintString(const char*, bool restrictedEntitySet); // prints out, after detecting entities. - - virtual bool VisitEnter(const tinyxml2::XMLDocument& /*doc*/); - virtual bool VisitExit(const tinyxml2::XMLElement& element); + bool CompactMode(const tinyxml2::XMLElement& element) override { return tinyxml2::XMLPrinter::CompactMode(element); } // Add VFS friendly implementations of output functions void Print(const char* format, ...) override; void Write(const char* data, size_t size) override; inline void Write(const char* data) { Write(data, strlen(data)); } void Putc(char ch) override; + void PrintSpace(int depth) override { tinyxml2::XMLPrinter::PrintSpace(depth); } // Overwrite Visitation of elements to add newlines before attributes - virtual bool VisitEnter(const tinyxml2::XMLElement& element, const tinyxml2::XMLAttribute* attribute); void PushAttribute(const char* name, const char* value, bool compactMode); // Accept a virtual FileStream instead of a FILE pointer @@ -63,10 +60,12 @@ public: int _depth; bool _processEntities; - enum { + enum + { ENTITY_RANGE = 64, BUF_SIZE = 200 }; + bool _entityFlag[ENTITY_RANGE]; bool _restrictedEntityFlag[ENTITY_RANGE]; }; @@ -152,4 +151,100 @@ public: } }; +class PrettyXMLPrinter : public tinyxml2::XMLPrinter +{ + // Re-implement private functionality in TinyXML2 + static const char LINE_FEED = static_cast(0x0a); // all line endings are normalized to LF + static const char LF = LINE_FEED; + static const char CARRIAGE_RETURN = static_cast(0x0d); // CR gets filtered out + static const char CR = CARRIAGE_RETURN; + static const char SINGLE_QUOTE = '\''; + static const char DOUBLE_QUOTE = '\"'; + + struct Entity + { + const char* pattern; + int length; + char value; + }; + + static const int NUM_ENTITIES = 5; + static constexpr Entity entities[NUM_ENTITIES] = { + {"quot", 4, DOUBLE_QUOTE}, + {"amp", 3, '&'}, + {"apos", 4, SINGLE_QUOTE}, + {"lt", 2, '<'}, + {"gt", 2, '>'} + }; +public: + PrettyXMLPrinter(VfsXMLPrinter& innerPrinter, int depth = 0); + + /// Visit a document. + virtual bool VisitEnter(const tinyxml2::XMLDocument& doc) + { + _processEntities = doc.ProcessEntities(); + return mInnerPrinter.VisitEnter(doc); + } + + /// Visit a document. + virtual bool VisitExit(const tinyxml2::XMLDocument& doc) + { + return mInnerPrinter.VisitExit(doc); + } + + /// Visit an element. + virtual bool VisitEnter(const tinyxml2::XMLElement& element, const tinyxml2::XMLAttribute* firstAttribute); + /// Visit an element. + virtual bool VisitExit(const tinyxml2::XMLElement& element) + { + _depth--; + return mInnerPrinter.VisitExit(element); + } + + /// Visit a declaration. + virtual bool Visit(const tinyxml2::XMLDeclaration& declaration) + { + return mInnerPrinter.Visit(declaration); + } + + /// Visit a text node. + virtual bool Visit(const tinyxml2::XMLText& text) + { + return mInnerPrinter.Visit(text); + } + + /// Visit a comment node. + virtual bool Visit(const tinyxml2::XMLComment& comment) + { + return mInnerPrinter.Visit(comment); + } + + /// Visit an unknown node. + virtual bool Visit(const tinyxml2::XMLUnknown& unknown) + { + return mInnerPrinter.Visit(unknown); + } + + void PushAttribute(const char* name, const char* value, bool compactMode); + + // Re-implement private functionality in TinyXML2 library, this is just a copy-paste job + void PrintString(const char*, bool restrictedEntitySet); // prints out, after detecting entities. + VfsXMLPrinter& mInnerPrinter; + + // Track private fields that are necessary for private functionality in TinyXML2 + int _depth; + bool _processEntities; + bool _compactMode; + + enum + { + ENTITY_RANGE = 64, + BUF_SIZE = 200 + }; + + bool _entityFlag[ENTITY_RANGE]; + bool _restrictedEntityFlag[ENTITY_RANGE]; +}; + + #endif //_FSTINYXML_H_