mxwcore-legion/dep/g3dlite/source/XML.cpp

217 lines
5.7 KiB
C++
Raw Permalink Normal View History

2023-11-05 15:26:19 -05:00
/**
\file XML.h
\author Morgan McGuire
\maintainer Morgan McGuire
\created 2010-02-11
\edited 2010-02-24
Copyright 2000-2012, Morgan McGuire.
All rights reserved.
*/
#include "G3D/XML.h"
#include "G3D/fileutils.h"
#include "G3D/TextInput.h"
#include "G3D/TextOutput.h"
#include "G3D/stringutils.h"
namespace G3D {
XML::XML(TextInput& t) : m_type(VALUE) {
deserialize(t);
}
double XML::number() const {
return TextInput::parseNumber(m_value);
}
bool XML::boolean() const {
return TextInput::parseBoolean(m_value);
}
void XML::load(const std::string& filename) {
TextInput::Settings s;
s.cppBlockComments = false;
s.cppLineComments = false;
s.proofSymbols = false;
TextInput t(filename, s);
deserialize(t);
}
void XML::save(const std::string& filename) const {
std::string s;
unparse(s);
writeWholeFile(filename, s);
}
void XML::unparse(std::string &s) const {
TextOutput::Settings set;
set.wordWrap = TextOutput::Settings::WRAP_WITHOUT_BREAKING;
TextOutput t(set);
serialize(t);
t.commitString(s);
}
void XML::serialize(TextOutput& t) const {
if (m_type == VALUE) {
// Raw string; no quotes
t.writeSymbol(m_value);
} else {
t.printf("<%s", m_name.c_str());
for (AttributeTable::Iterator it = m_attribute.begin(); it.isValid(); ++it) {
t.printf(" %s=\"%s\"", it->key.c_str(), it->value.m_value.c_str());
}
t.printf(">");
t.writeNewline();
t.pushIndent();
for (int i = 0; i < m_child.size(); ++i) {
m_child[i].serialize(t);
if (m_child[i].m_type == VALUE) {
// Only tags know to append a newline
t.writeNewline();
}
}
t.popIndent();
t.printf("</%s>", m_name.c_str());
t.writeNewline();
}
}
void XML::parse(const std::string& s) {
TextInput t(TextInput::FROM_STRING, s);
deserialize(t);
}
/** True if the next token begins the close tag */
static bool atClose(TextInput& t, const std::string name) {
if ((t.peek().type() == Token::SYMBOL) && (t.peek().string() == "<")) {
// Need to keep looking ahead
Token p0 = t.read();
if ((t.peek().type() == Token::SYMBOL) && (t.peek().string() == "/")) {
// Check the name on the close tag. It *must* match if
// this is a well-formed document, but there might be a
// tag error.
Token p1 = t.read();
Token p2 = t.peek();
std::string s = p2.string();
debugAssertM(beginsWith(name, s), "Mismatched close tag");
// Put the tokens back
t.push(p1);
t.push(p0);
return true;
} else {
// Put the read token back
t.push(p0);
return false;
}
} else {
return false;
}
}
void XML::deserialize(TextInput& t) {
begin:
Token n = t.read();
m_attribute.clear();
m_child.clear();
m_name = "";
m_value = "";
if ((n.type() == Token::SYMBOL) && (n.string() == "<")) {
// Beginning a tag
// Read name
n = t.read();
debugAssert(n.type() == Token::SYMBOL);
bool isComment =
(n.string() == "!") &&
(t.peek().type() == Token::SYMBOL) &&
(t.peek().string() == "--");
// ignored tag: <?xml> or <!xml>
// comment tag: <!-- ... -->
if ((n.string() == "?") || ((n.string() == "!") && ! isComment)) {
// Ignore this tag
while (t.hasMore() && ! ((n.type() == Token::SYMBOL) && (n.string() == ">"))) {
n = t.read();
}
goto begin;
} else if (isComment) {
// Ignore until "-->"
bool prevWasDash = false;
while (t.hasMore() && ! ((n.type() == Token::SYMBOL) && (n.string() == ">") && prevWasDash)) {
prevWasDash = (n.type() == Token::SYMBOL) && (n.string() == "--");
n = t.read();
}
goto begin;
}
// Keep reading until no colon
m_name += n.string();
n = t.read();
while ((n.type() == Token::SYMBOL) && (n.string() == ":")) {
// tag with namespace: <x:y>
m_name += ":" + t.readSymbol();
n = t.read();
}
// Read end of tag/close
bool done = false;
while (! done) {
debugAssert(n.type() == Token::SYMBOL);
if (n.string() == "/") {
// empty-element tag: <foo/>
// Consume the close tag
t.readSymbol(">");
done = true;
} else if (n.string() == ">") {
// End of open tag: read children until close tag
while (! atClose(t, m_name)) {
m_child.next().deserialize(t);
}
// Read close tag (we wouldn't be here unless it parses correctly)
while (t.hasMore() && ! (t.readSymbol() == ">")) {}
done = true;
} else {
// Attribute pair
std::string k = n.string();
t.readSymbol("=");
std::string v = t.read().string();
m_attribute.set(k, v);
// Advance to next
n = t.read();
}
}
} else {
// Beginning embedded content. Read until the end of file or the next tag.
m_type = VALUE;
m_value += n.string();
n = t.peek();
while (t.hasMore() && ! ((n.type() == Token::SYMBOL) && (n.string() == "<"))) {
m_value += " " + t.read().string();
n = t.peek();
}
}
}
}