mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-16 00:54:54 +00:00
Improve tinyXml2 output formatting
This commit is contained in:
parent
3812ce2e82
commit
165459c90b
2 changed files with 308 additions and 139 deletions
|
|
@ -26,6 +26,182 @@
|
||||||
|
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Re-implement private functionality in TinyXML2
|
||||||
|
|
||||||
|
static const char LINE_FEED = static_cast<char>(0x0a); // all line endings are normalized to LF
|
||||||
|
static const char LF = LINE_FEED;
|
||||||
|
static const char CARRIAGE_RETURN = static_cast<char>(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)
|
||||||
|
{
|
||||||
|
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<unsigned char>(entityValue);
|
||||||
|
TIXMLASSERT(flagIndex < ENTITY_RANGE);
|
||||||
|
_entityFlag[flagIndex] = true;
|
||||||
|
}
|
||||||
|
_restrictedEntityFlag[static_cast<unsigned char>('&')] = true;
|
||||||
|
_restrictedEntityFlag[static_cast<unsigned char>('<')] = true;
|
||||||
|
_restrictedEntityFlag[static_cast<unsigned char>('>')] = true; // not required, but consistency is nice
|
||||||
|
}
|
||||||
|
|
||||||
|
VfsXMLPrinter::~VfsXMLPrinter()
|
||||||
|
{
|
||||||
|
m_Stream.flush();
|
||||||
|
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<unsigned char>(*q)]) {
|
||||||
|
while (p < q) {
|
||||||
|
const size_t delta = q - p;
|
||||||
|
const int toPrint = (INT_MAX < delta) ? INT_MAX : static_cast<int>(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<int>(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
|
||||||
|
|
||||||
|
void VfsXMLPrinter::Print(const char* format, ...)
|
||||||
|
{
|
||||||
|
va_list va;
|
||||||
|
va_start(va, format);
|
||||||
|
|
||||||
|
m_Stream.writeFormattedBuffer(format, va);
|
||||||
|
|
||||||
|
va_end(va);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VfsXMLPrinter::Write(const char* data, size_t size)
|
||||||
|
{
|
||||||
|
m_Stream.write(size, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VfsXMLPrinter::Putc(char ch)
|
||||||
|
{
|
||||||
|
m_Stream.write(static_cast<U8>(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)
|
bool VfsXMLDocument::LoadFile(const char* pFilename)
|
||||||
{
|
{
|
||||||
// Expand the file-path.
|
// Expand the file-path.
|
||||||
|
|
@ -61,96 +237,6 @@ bool VfsXMLDocument::LoadFile(const char* pFilename)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VfsXMLDocument::SaveFile(const char* pFilename)
|
|
||||||
{
|
|
||||||
// Expand the file-name into the file-path buffer.
|
|
||||||
char filenameBuffer[1024];
|
|
||||||
Con::expandScriptFilename(filenameBuffer, sizeof(filenameBuffer), pFilename);
|
|
||||||
|
|
||||||
FileStream stream;
|
|
||||||
|
|
||||||
// File opened?
|
|
||||||
if (!stream.open(filenameBuffer, Torque::FS::File::Write))
|
|
||||||
{
|
|
||||||
// No, so warn.
|
|
||||||
Con::warnf("Taml::writeFile() - Could not open filename '%s' for write.", filenameBuffer);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ret = SaveFile(stream);
|
|
||||||
|
|
||||||
stream.close();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VfsXMLDocument::ClearError()
|
|
||||||
{
|
|
||||||
_errorID = tinyxml2::XML_SUCCESS;
|
|
||||||
_errorLineNum = 0;
|
|
||||||
_errorStr.Reset();
|
|
||||||
|
|
||||||
tinyxml2::XMLDocument::ClearError();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VfsXMLDocument::SetError(tinyxml2::XMLError error, int lineNum, const char* format, ...)
|
|
||||||
{
|
|
||||||
TIXMLASSERT(error >= 0 && error < tinyxml2::XML_ERROR_COUNT);
|
|
||||||
_errorID = error;
|
|
||||||
_errorLineNum = lineNum;
|
|
||||||
_errorStr.Reset();
|
|
||||||
|
|
||||||
const size_t BUFFER_SIZE = 1000;
|
|
||||||
char* buffer = new char[BUFFER_SIZE];
|
|
||||||
|
|
||||||
TIXMLASSERT(sizeof(error) <= sizeof(int));
|
|
||||||
dSprintf(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
|
|
||||||
|
|
||||||
if (format) {
|
|
||||||
size_t len = strlen(buffer);
|
|
||||||
dSprintf(buffer + len, BUFFER_SIZE - len, ": ");
|
|
||||||
len = strlen(buffer);
|
|
||||||
|
|
||||||
va_list va;
|
|
||||||
va_start(va, format);
|
|
||||||
dSprintf(buffer + len, BUFFER_SIZE - len, format, va);
|
|
||||||
va_end(va);
|
|
||||||
}
|
|
||||||
_errorStr.SetStr(buffer);
|
|
||||||
delete[] buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
VfsXMLPrinter::VfsXMLPrinter(FileStream& stream, bool compact, int depth)
|
|
||||||
: XMLPrinter(NULL, compact, depth),
|
|
||||||
m_Stream(stream)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
VfsXMLPrinter::~VfsXMLPrinter()
|
|
||||||
{
|
|
||||||
m_Stream.flush();
|
|
||||||
m_Stream.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VfsXMLPrinter::Print(const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list va;
|
|
||||||
va_start(va, format);
|
|
||||||
|
|
||||||
m_Stream.writeFormattedBuffer(format, va);
|
|
||||||
|
|
||||||
va_end(va);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VfsXMLPrinter::Write(const char* data, size_t size)
|
|
||||||
{
|
|
||||||
m_Stream.write(size, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VfsXMLPrinter::Putc(char ch)
|
|
||||||
{
|
|
||||||
m_Stream.write(static_cast<U8>(ch));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VfsXMLDocument::LoadFile(FileStream& stream)
|
bool VfsXMLDocument::LoadFile(FileStream& stream)
|
||||||
{
|
{
|
||||||
// Delete the existing data:
|
// Delete the existing data:
|
||||||
|
|
@ -257,3 +343,61 @@ bool VfsXMLDocument::SaveFile(FileStream& stream)
|
||||||
Print(&printer);
|
Print(&printer);
|
||||||
return !Error();
|
return !Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VfsXMLDocument::SaveFile(const char* pFilename)
|
||||||
|
{
|
||||||
|
// Expand the file-name into the file-path buffer.
|
||||||
|
char filenameBuffer[1024];
|
||||||
|
Con::expandScriptFilename(filenameBuffer, sizeof(filenameBuffer), pFilename);
|
||||||
|
|
||||||
|
FileStream stream;
|
||||||
|
|
||||||
|
// File opened?
|
||||||
|
if (!stream.open(filenameBuffer, Torque::FS::File::Write))
|
||||||
|
{
|
||||||
|
// No, so warn.
|
||||||
|
Con::warnf("Taml::writeFile() - Could not open filename '%s' for write.", filenameBuffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ret = SaveFile(stream);
|
||||||
|
|
||||||
|
stream.close();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VfsXMLDocument::ClearError()
|
||||||
|
{
|
||||||
|
_errorID = tinyxml2::XML_SUCCESS;
|
||||||
|
_errorLineNum = 0;
|
||||||
|
_errorStr.Reset();
|
||||||
|
|
||||||
|
tinyxml2::XMLDocument::ClearError();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VfsXMLDocument::SetError(tinyxml2::XMLError error, int lineNum, const char* format, ...)
|
||||||
|
{
|
||||||
|
TIXMLASSERT(error >= 0 && error < tinyxml2::XML_ERROR_COUNT);
|
||||||
|
_errorID = error;
|
||||||
|
_errorLineNum = lineNum;
|
||||||
|
_errorStr.Reset();
|
||||||
|
|
||||||
|
const size_t BUFFER_SIZE = 1000;
|
||||||
|
char* buffer = new char[BUFFER_SIZE];
|
||||||
|
|
||||||
|
TIXMLASSERT(sizeof(error) <= sizeof(int));
|
||||||
|
dSprintf(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
|
||||||
|
|
||||||
|
if (format) {
|
||||||
|
size_t len = strlen(buffer);
|
||||||
|
dSprintf(buffer + len, BUFFER_SIZE - len, ": ");
|
||||||
|
len = strlen(buffer);
|
||||||
|
|
||||||
|
va_list va;
|
||||||
|
va_start(va, format);
|
||||||
|
dSprintf(buffer + len, BUFFER_SIZE - len, format, va);
|
||||||
|
va_end(va);
|
||||||
|
}
|
||||||
|
_errorStr.SetStr(buffer);
|
||||||
|
delete[] buffer;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,35 @@ public:
|
||||||
VfsXMLPrinter(FileStream& stream, bool compact = false, int depth = 0);
|
VfsXMLPrinter(FileStream& stream, bool compact = false, int depth = 0);
|
||||||
~VfsXMLPrinter() override;
|
~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);
|
||||||
|
|
||||||
|
// Add VFS friendly implementations of output functions
|
||||||
void Print(const char* format, ...) override;
|
void Print(const char* format, ...) override;
|
||||||
void Write(const char* data, size_t size) 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 Putc(char ch) override;
|
||||||
|
|
||||||
|
// 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
|
||||||
FileStream& m_Stream;
|
FileStream& m_Stream;
|
||||||
|
|
||||||
|
// Track private fields that are necessary for private functionality in TinyXML2
|
||||||
|
int _depth;
|
||||||
|
bool _processEntities;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ENTITY_RANGE = 64,
|
||||||
|
BUF_SIZE = 200
|
||||||
|
};
|
||||||
|
bool _entityFlag[ENTITY_RANGE];
|
||||||
|
bool _restrictedEntityFlag[ENTITY_RANGE];
|
||||||
};
|
};
|
||||||
|
|
||||||
class VfsXMLDocument : public tinyxml2::XMLDocument
|
class VfsXMLDocument : public tinyxml2::XMLDocument
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue