583 lines
19 KiB
C++
583 lines
19 KiB
C++
|
/*
|
||
|
* Portable Agile C++ Classes (PACC)
|
||
|
* Copyright (C) 2001-2003 by Marc Parizeau
|
||
|
* http://manitou.gel.ulaval.ca/~parizeau/PACC
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Lesser General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2.1 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public
|
||
|
* License along with this library; if not, write to the Free Software
|
||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
*
|
||
|
* Contact:
|
||
|
* Laboratoire de Vision et Systemes Numeriques
|
||
|
* Departement de genie electrique et de genie informatique
|
||
|
* Universite Laval, Quebec, Canada, G1K 7P4
|
||
|
* http://vision.gel.ulaval.ca
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/*!
|
||
|
* \file PACC/XML/Node.cpp
|
||
|
* \brief Class methods for the %XML parse tree node.
|
||
|
* \author Marc Parizeau, Laboratoire de vision et systèmes numériques, Université Laval
|
||
|
* $Revision: 1.7.2.2 $
|
||
|
* $Date: 2007/09/10 18:24:10 $
|
||
|
*/
|
||
|
|
||
|
#include "XML/Node.hpp"
|
||
|
#include "XML/Iterator.hpp"
|
||
|
#include "XML/Streamer.hpp"
|
||
|
#include "Util/Assert.hpp"
|
||
|
#include <sstream>
|
||
|
#include <stdexcept>
|
||
|
|
||
|
using namespace std;
|
||
|
using namespace PACC;
|
||
|
|
||
|
map<string,char> XML::Node::smMap;
|
||
|
|
||
|
/*!
|
||
|
*/
|
||
|
XML::Node::Node(void) : mType(eRoot) {
|
||
|
mParent = mFirstChild = mLastChild = mPrevSibling = mNextSibling = NULL;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
*/
|
||
|
XML::Node::Node(const string& inValue, XML::NodeType inType) : mType(inType) {
|
||
|
(*this)[""] = inValue;
|
||
|
mParent = mFirstChild = mLastChild = mPrevSibling = mNextSibling = NULL;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
*/
|
||
|
XML::Node::Node(const string& inValue, const XML::AttributeList& inAttrList) : AttributeList(inAttrList), mType(eData) {
|
||
|
(*this)[""] = inValue;
|
||
|
mParent = mFirstChild = mLastChild = mPrevSibling = mNextSibling = NULL;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
*/
|
||
|
XML::Node::Node(const XML::Node& inNode) : AttributeList() {
|
||
|
mParent = mFirstChild = mLastChild = mPrevSibling = mNextSibling = NULL;
|
||
|
operator=(inNode);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
This method recursively deletes all of its children.
|
||
|
*/
|
||
|
XML::Node::~Node(void) {
|
||
|
// delete all child nodes
|
||
|
eraseChildren();
|
||
|
// detach from parent and siblings
|
||
|
detachFromSiblingsAndParent();
|
||
|
// cleanup node pointers
|
||
|
mParent = mFirstChild = mLastChild = mPrevSibling = mNextSibling = NULL;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\return A reference to this node
|
||
|
\attention The copied tree must not be a sub-tree of this node. Otherwise, the internal tree structure will become corrupted.
|
||
|
*/
|
||
|
XML::Node& XML::Node::operator=(const Node& inRoot)
|
||
|
{
|
||
|
// do not self assign!
|
||
|
if(&inRoot == this) return *this;
|
||
|
// delete all child nodes
|
||
|
XML::Iterator lChild = getFirstChild();
|
||
|
while(lChild) delete &(*(lChild++));
|
||
|
// fix child pointers
|
||
|
mFirstChild = mLastChild = NULL;
|
||
|
// assign type and attributes
|
||
|
mType = inRoot.mType;
|
||
|
map<string,string>::operator=(inRoot);
|
||
|
// copy all children of inRoot
|
||
|
for(XML::ConstIterator lNode = inRoot.getFirstChild(); lNode; ++lNode) {
|
||
|
// allocate and copy node
|
||
|
Node* lChildNode = new Node(*lNode);
|
||
|
// is this the first child?
|
||
|
if(mFirstChild == NULL) mFirstChild = mLastChild = lChildNode;
|
||
|
else {
|
||
|
//adjust sibling pointers
|
||
|
mLastChild->mNextSibling = lChildNode;
|
||
|
lChildNode->mPrevSibling = mLastChild;
|
||
|
mLastChild = lChildNode;
|
||
|
}
|
||
|
// adjust parent pointer
|
||
|
lChildNode->mParent = this;
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\return A reference to the converted string.
|
||
|
The default quotes are "&", "<", ">", "'", and """. Argument \c ioMap can be used to specify any conversion table.
|
||
|
*/
|
||
|
string& XML::Node::convertFromQuotes(string& ioString, map<string,char>& ioMap)
|
||
|
{
|
||
|
if(ioMap.empty()) {
|
||
|
// initialize quote list
|
||
|
ioMap["amp"] = '&';
|
||
|
ioMap["lt"] = '<';
|
||
|
ioMap["gt"] = '>';
|
||
|
ioMap["apos"] = '\'';
|
||
|
ioMap["quot"] = '"';
|
||
|
}
|
||
|
string::size_type lStart, lEnd = 0;
|
||
|
while((lStart = ioString.find('&', lEnd)) < ioString.size() &&
|
||
|
(lEnd = ioString.find(';', lStart)) < ioString.size())
|
||
|
{
|
||
|
string lToken = ioString.substr(lStart+1, lEnd-lStart-1);
|
||
|
if(ioMap.find(lToken) != ioMap.end()) {
|
||
|
ioString[lStart] = ioMap[lToken];
|
||
|
ioString.erase(lStart+1, lEnd-lStart);
|
||
|
lEnd = lStart+1;
|
||
|
}
|
||
|
}
|
||
|
return ioString;
|
||
|
}
|
||
|
|
||
|
/*!\return A pointer to this node.
|
||
|
This method removes this node from its parent tree. The list of sibling nodes is repaired accordingly.
|
||
|
*/
|
||
|
XML::Node* XML::Node::detachFromSiblingsAndParent(void) {
|
||
|
// adjust sibling list
|
||
|
if(mPrevSibling) mPrevSibling->mNextSibling = mNextSibling;
|
||
|
if(mNextSibling) mNextSibling->mPrevSibling = mPrevSibling;
|
||
|
if(mParent) {
|
||
|
// adjust parent first and last child pointers
|
||
|
if(mParent->mFirstChild == this) mParent->mFirstChild = mNextSibling;
|
||
|
if(mParent->mLastChild == this) mParent->mLastChild = mPrevSibling;
|
||
|
}
|
||
|
mPrevSibling = mNextSibling = mParent = NULL;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
*/
|
||
|
void XML::Node::eraseChildren(void)
|
||
|
{
|
||
|
// delete all child nodes
|
||
|
XML::Iterator lChild = getFirstChild();
|
||
|
while(lChild) delete &(*(lChild++));
|
||
|
}
|
||
|
|
||
|
/*!\return The number of child nodes.
|
||
|
*/
|
||
|
unsigned int XML::Node::getChildCount(void) const {
|
||
|
unsigned int lCount = 0;
|
||
|
for(ConstIterator lChild = getFirstChild(); lChild; ++lChild) ++lCount;
|
||
|
return lCount;
|
||
|
}
|
||
|
|
||
|
/*!\return A pointer to the inserted child node.
|
||
|
*/
|
||
|
XML::Node* XML::Node::insertAsLastChild(XML::Node* inChild) {
|
||
|
PACC_AssertM(inChild, "Cannot add null pointer node");
|
||
|
PACC_AssertM(!inChild->mParent && !inChild->mPrevSibling && !inChild->mNextSibling, "Node must be detached before it can be added!");
|
||
|
// is this new child the first?
|
||
|
if(mFirstChild == NULL) mFirstChild = inChild;
|
||
|
else {
|
||
|
// insert after last
|
||
|
inChild->mPrevSibling = mLastChild;
|
||
|
mLastChild->mNextSibling = inChild;
|
||
|
}
|
||
|
// adjust parent pointers
|
||
|
inChild->mParent = this;
|
||
|
mLastChild = inChild;
|
||
|
return inChild;
|
||
|
}
|
||
|
|
||
|
/*!\return A pointer to the inserted sibling node.
|
||
|
*/
|
||
|
XML::Node* XML::Node::insertAsPreviousSibling(XML::Node* inSibling) {
|
||
|
PACC_AssertM(inSibling, "Cannot insert null pointer node");
|
||
|
PACC_AssertM(!inSibling->mParent && !inSibling->mPrevSibling && !inSibling->mNextSibling, "Node must be detached before it can be inserted!");
|
||
|
// is this new sibling the first?
|
||
|
if(mPrevSibling == NULL) {
|
||
|
inSibling->mNextSibling = this;
|
||
|
mPrevSibling = inSibling;
|
||
|
// adjust first child of parent
|
||
|
if(mParent) mParent->mFirstChild = inSibling;
|
||
|
} else {
|
||
|
// this node is neither the first or the last
|
||
|
mPrevSibling->mNextSibling = inSibling;
|
||
|
inSibling->mPrevSibling = mPrevSibling;
|
||
|
inSibling->mNextSibling = this;
|
||
|
mPrevSibling = inSibling;
|
||
|
}
|
||
|
// adjust parent pointer
|
||
|
inSibling->mParent = mParent;
|
||
|
return inSibling;
|
||
|
}
|
||
|
|
||
|
/*!\return A node pointer to the parsed element.
|
||
|
Any tag name defined in \c inNoParseTags will be treated as if its content is a string token (content will not be parsed).
|
||
|
*/
|
||
|
XML::Node* XML::Node::parse(PACC::Tokenizer& inTokenizer, const set<string>& inNoParseTags)
|
||
|
{
|
||
|
Node* lNode = NULL;
|
||
|
// look for start tag
|
||
|
string lToken;
|
||
|
inTokenizer.setDelimiters("", "<");
|
||
|
if(!inTokenizer.getNextToken(lToken)) return 0;
|
||
|
// remove any leading white space
|
||
|
size_type lPos = lToken.find_first_not_of(" \t\r\n");
|
||
|
if(lPos == string::npos) {
|
||
|
if(!inTokenizer.getNextToken(lToken)) return 0;
|
||
|
} else if(lPos > 0) lToken.erase(0, lPos);
|
||
|
if(lToken[0] == '<') {
|
||
|
// check for end tag
|
||
|
if(inTokenizer.peekNextChar() == '/') {
|
||
|
// found end tag;
|
||
|
inTokenizer.setDelimiters("", "/");
|
||
|
inTokenizer.getNextToken(lToken);
|
||
|
return 0;
|
||
|
}
|
||
|
// found start tag
|
||
|
lNode = new Node;
|
||
|
lNode->parseStartTag(inTokenizer, lToken);
|
||
|
if(lToken[0] == '/') {
|
||
|
// found end tag; next token must be '>'
|
||
|
inTokenizer.setDelimiters("", ">");
|
||
|
if(!inTokenizer.getNextToken(lToken)) lNode->throwError(inTokenizer, "unexpected eof");
|
||
|
if(lToken[0] != '>') lNode->throwError(inTokenizer, "invalid start tag");
|
||
|
} else if(lNode->getType() == eData) {
|
||
|
// either read or parse tag content
|
||
|
if(inNoParseTags.find((*lNode)[""]) != inNoParseTags.end()) {
|
||
|
lNode->readContentAsString(inTokenizer);
|
||
|
} else {
|
||
|
Node* lChild;
|
||
|
// parse all child
|
||
|
while((lChild=parse(inTokenizer, inNoParseTags)) != NULL) lNode->insertAsLastChild(lChild);
|
||
|
// test for valid end tag
|
||
|
inTokenizer.setDelimiters("", " \t\n\r>");
|
||
|
if(!inTokenizer.getNextToken(lToken)) lNode->throwError(inTokenizer, "unexpected eof");
|
||
|
if(lToken != (*lNode)[""]) lNode->throwError(inTokenizer, "invalid end tag");
|
||
|
}
|
||
|
// next token must be '>'
|
||
|
inTokenizer.setDelimiters(" \t\n\r", ">");
|
||
|
if(!inTokenizer.getNextToken(lToken)) lNode->throwError(inTokenizer, "unexpected eof");
|
||
|
if(lToken[0] != '>') lNode->throwError(inTokenizer, "invalid end tag");
|
||
|
}
|
||
|
// else node is not markup
|
||
|
} else {
|
||
|
// found a simple string node
|
||
|
lNode = new Node;
|
||
|
lNode->mType = eString;
|
||
|
// remove any ending white space
|
||
|
lPos = lToken.find_last_not_of(" \t\r\n");
|
||
|
PACC_AssertM(lPos != string::npos, "Internal error!");
|
||
|
if(lPos < lToken.size()-1) lToken.resize(lPos+1);
|
||
|
// convert basic quotes
|
||
|
(*lNode)[""] = convertFromQuotes(lToken);
|
||
|
}
|
||
|
return lNode;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Ending token is returned through argument \c outToken.
|
||
|
*/
|
||
|
void XML::Node::parseAttributeList(PACC::Tokenizer& inTokenizer, string& outToken)
|
||
|
{
|
||
|
inTokenizer.setDelimiters(" \t\n\r", "=/?>");
|
||
|
// next token should be an attribute name
|
||
|
if(!inTokenizer.getNextToken(outToken)) throwError(inTokenizer, "unexpected eof");
|
||
|
// parse all attributes
|
||
|
while(outToken[0] != '>' && outToken[0] != '/' && outToken[0] != '?')
|
||
|
{
|
||
|
if(outToken[0] == '=') throwError(inTokenizer, "missing attribute name");
|
||
|
// ok, found an attribute name!
|
||
|
string lName = outToken;
|
||
|
// next token should be '='
|
||
|
inTokenizer.setDelimiters(" \t\n\r", "=");
|
||
|
if(!inTokenizer.getNextToken(outToken) || outToken[0] != '=')
|
||
|
throwError(inTokenizer, "invalid attribute");
|
||
|
inTokenizer.setDelimiters(" \t\n\r", "'\"");
|
||
|
// next token must be '"' or "'"
|
||
|
if(!inTokenizer.getNextToken(outToken)) throwError(inTokenizer, "unexpected eof");
|
||
|
string lValue;
|
||
|
switch(outToken[0]) {
|
||
|
case '\'':
|
||
|
inTokenizer.setDelimiters("", "'");
|
||
|
if(!inTokenizer.getNextToken(outToken)) throwError(inTokenizer, "unexpected eof");
|
||
|
if(outToken[0] != '\'') {
|
||
|
lValue = outToken;
|
||
|
if(!inTokenizer.getNextToken(outToken)) throwError(inTokenizer, "unexpected eof");
|
||
|
}
|
||
|
break;
|
||
|
case '"':
|
||
|
inTokenizer.setDelimiters("", "\"");
|
||
|
if(!inTokenizer.getNextToken(outToken)) throwError(inTokenizer, "unexpected eof");
|
||
|
if(outToken[0] != '"') {
|
||
|
lValue = outToken;
|
||
|
if(!inTokenizer.getNextToken(outToken)) throwError(inTokenizer, "unexpected eof");
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
throwError(inTokenizer, "invalid attribute value");
|
||
|
}
|
||
|
// insert attribute
|
||
|
(*this)[lName] = convertFromQuotes(lValue);
|
||
|
inTokenizer.setDelimiters(" \t\n\r", "=/?>");
|
||
|
if(!inTokenizer.getNextToken(outToken)) throwError(inTokenizer, "unexpected eof");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
This method assumes that token "<" has already been read. It returns the ending token through argument \c outToken.
|
||
|
*/
|
||
|
void XML::Node::parseStartTag(PACC::Tokenizer& inTokenizer, string& outToken)
|
||
|
{
|
||
|
// parse tag name
|
||
|
inTokenizer.setDelimiters("", " \t\n\r/>");
|
||
|
if(!inTokenizer.getNextToken(outToken)) throwError(inTokenizer, "unexpected eof");
|
||
|
if(outToken.find_first_of(" \t\n\r/>") != string::npos) throwError(inTokenizer, "invalid start tag");
|
||
|
string& lValue = (*this)[""];
|
||
|
switch(outToken[0]) {
|
||
|
case '!':
|
||
|
if(outToken.size() >= 3 && outToken[1] == '-' && outToken[2] == '-') {
|
||
|
// process comment
|
||
|
mType = eComment;
|
||
|
outToken.erase(0, 3);
|
||
|
inTokenizer.setDelimiters("", ">");
|
||
|
do {
|
||
|
int lSize = outToken.size();
|
||
|
if(lSize > 2 && outToken[lSize-2] == '-' && outToken[lSize-1] == '-') {
|
||
|
lValue += outToken.erase(lSize-2, 2);
|
||
|
break;
|
||
|
} else lValue += outToken;
|
||
|
} while(inTokenizer.getNextToken(outToken));
|
||
|
if(!inTokenizer.getNextToken(outToken)) throwError(inTokenizer, "unexpected eof");
|
||
|
} else if(outToken.size() >= 8 && memcmp(outToken.data()+1, "[CDATA[", 7) == 0) {
|
||
|
// process cdata section
|
||
|
mType = eCDATA;
|
||
|
outToken.erase(0, 8);
|
||
|
inTokenizer.setDelimiters("", ">");
|
||
|
do {
|
||
|
int lSize = outToken.size();
|
||
|
if(lSize >= 2 && outToken[lSize-2] == ']' && outToken[lSize-1] == ']') {
|
||
|
lValue += outToken.erase(lSize-2, 2);
|
||
|
break;
|
||
|
} else lValue += outToken;
|
||
|
} while(inTokenizer.getNextToken(outToken));
|
||
|
if(!inTokenizer.getNextToken(outToken)) throwError(inTokenizer, "unexpected eof");
|
||
|
} else {
|
||
|
// process special element (doctype, attribute, etc.)
|
||
|
mType = eSpecial;
|
||
|
lValue = outToken.erase(0, 1);
|
||
|
inTokenizer.setDelimiters("", ">");
|
||
|
if(!inTokenizer.getNextToken(outToken)) throwError(inTokenizer, "unexpected eof");
|
||
|
if(outToken[0] != '>') {
|
||
|
lValue += outToken;
|
||
|
if(!inTokenizer.getNextToken(outToken)) throwError(inTokenizer, "unexpected eof");
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case '?':
|
||
|
if(outToken == "?xml") {
|
||
|
// process xml declaration
|
||
|
mType = eDecl;
|
||
|
lValue = outToken.erase(0, 1);
|
||
|
parseAttributeList(inTokenizer, outToken);
|
||
|
if(outToken[0] != '?') throwError(inTokenizer, "invalid xml declaration");
|
||
|
if(!inTokenizer.getNextToken(outToken)) throwError(inTokenizer, "unexpected eof");
|
||
|
if(outToken[0] != '>') throwError(inTokenizer, "invalid xml declaration");
|
||
|
} else {
|
||
|
// process special processing instruction
|
||
|
mType = ePI;
|
||
|
lValue = outToken.substr(1, outToken.size()-1);
|
||
|
inTokenizer.setDelimiters("", "?>");
|
||
|
while(inTokenizer.getNextToken(outToken)) {
|
||
|
if(outToken[0] == '?') {
|
||
|
if(!inTokenizer.getNextToken(outToken)) throwError(inTokenizer, "unexpected eof");
|
||
|
if(outToken[0] == '>') break;
|
||
|
else lValue += '?';
|
||
|
}
|
||
|
lValue += outToken;
|
||
|
}
|
||
|
if(outToken.empty()) throwError(inTokenizer, "unexpected eof");
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
// process data markup
|
||
|
mType = eData;
|
||
|
lValue = outToken;
|
||
|
parseAttributeList(inTokenizer, outToken);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
*/
|
||
|
void XML::Node::readContentAsString(PACC::Tokenizer& inTokenizer)
|
||
|
{
|
||
|
// create child node
|
||
|
Node* lChild = new Node;
|
||
|
insertAsLastChild(lChild);
|
||
|
lChild->setType(eNoParse);
|
||
|
// parse until end tag
|
||
|
inTokenizer.setDelimiters("", "<>");
|
||
|
string lToken;
|
||
|
int lCount = 1;
|
||
|
const string& lTag = (*this)[""];
|
||
|
string& lString = (*lChild)[""];
|
||
|
while(lCount > 0) {
|
||
|
// check every start tag
|
||
|
if(!inTokenizer.getNextToken(lToken)) throwError(inTokenizer, string("unexpected eof"));
|
||
|
if(lToken[0] == '<') {
|
||
|
if(!inTokenizer.getNextToken(lToken)) throwError(inTokenizer, string("unexpected eof"));
|
||
|
if(lToken[0] == '/' && memcmp(lToken.data()+1, lTag.data(), lTag.size()) == 0) --lCount;
|
||
|
else if(lToken[lToken.size()-1] != '/' && memcmp(lToken.data(), lTag.data(), lTag.size()) == 0) ++lCount;
|
||
|
if(lCount > 0) {
|
||
|
lString += "<";
|
||
|
lString += lToken;
|
||
|
}
|
||
|
} else lString += lToken;
|
||
|
}
|
||
|
// remove any leading white space
|
||
|
size_type lPos = lString.find_first_not_of(" \t\r\n");
|
||
|
if(lPos == string::npos) {
|
||
|
// string is all white space
|
||
|
lString.clear();
|
||
|
} else {
|
||
|
// erase leading white space
|
||
|
lString.erase(0, lPos);
|
||
|
// remove any ending white space
|
||
|
lPos = lString.find_last_not_of(" \t\r\n");
|
||
|
PACC_AssertM(lPos != string::npos, "Internal error!");
|
||
|
// erase trailing white space
|
||
|
if(lPos < lString.size()-1) lString.resize(lPos+1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Argument \c inIndent is used to control indentation. By default (\c inIndent=true), the sub-tree rooted by this node will be serialized with indentation. If \c inIndent=false, then the node will be serialized without any form of indentation (including line feeds).
|
||
|
*/
|
||
|
void XML::Node::serialize(XML::Streamer& outStream, bool inIndent) const
|
||
|
{
|
||
|
switch(mType)
|
||
|
{
|
||
|
case eCDATA:
|
||
|
{
|
||
|
outStream.insertCDATA(getValue());
|
||
|
break;
|
||
|
}
|
||
|
case eComment:
|
||
|
{
|
||
|
outStream.insertComment(getValue());
|
||
|
break;
|
||
|
}
|
||
|
case eData:
|
||
|
{
|
||
|
// check for tag with single string content
|
||
|
ConstIterator lChild = getFirstChild();
|
||
|
if(lChild && lChild->mType == eString && !lChild->getNextSibling()) {
|
||
|
// disable indentation
|
||
|
outStream.openTag(getValue(), false);
|
||
|
} else {
|
||
|
outStream.openTag(getValue(), inIndent);
|
||
|
}
|
||
|
// serialize attribute list
|
||
|
for(map<string,string>::const_iterator i = begin(); i != end(); ++i) {
|
||
|
if(i->first != "") outStream.insertAttribute(i->first, i->second);
|
||
|
}
|
||
|
// serialize child nodes
|
||
|
while(lChild) (lChild++)->serialize(outStream, inIndent);
|
||
|
outStream.closeTag();
|
||
|
break;
|
||
|
}
|
||
|
case eNoParse:
|
||
|
{
|
||
|
outStream.insertStringContent(getValue(), false);
|
||
|
break;
|
||
|
}
|
||
|
case ePI:
|
||
|
{
|
||
|
string lValue = string("<?") + getValue() + string("?>");
|
||
|
outStream.insertStringContent(lValue, false);
|
||
|
break;
|
||
|
}
|
||
|
case eSpecial:
|
||
|
{
|
||
|
string lValue = string("<!") + getValue() + string(">");
|
||
|
outStream.insertStringContent(lValue, false);
|
||
|
break;
|
||
|
}
|
||
|
case eString:
|
||
|
{
|
||
|
outStream.insertStringContent(getValue(), true);
|
||
|
break;
|
||
|
}
|
||
|
case eDecl:
|
||
|
{
|
||
|
string lValue = "<?xml version=\"";
|
||
|
if(isDefined("version")) lValue += getAttribute("version");
|
||
|
else lValue += "1.0";
|
||
|
lValue += "\"";
|
||
|
if(isDefined("encoding")) lValue += " encoding=\"" + getAttribute("encoding") + "\"";
|
||
|
lValue += "?>";
|
||
|
outStream.insertStringContent(lValue, false);
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
PACC_AssertM(false, "Unknown node type!");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
*/
|
||
|
void XML::Node::throwError(PACC::Tokenizer& inTokenizer, const string& inMessage) const
|
||
|
{
|
||
|
ostringstream lStream;
|
||
|
lStream << "\nXML parse error";
|
||
|
if(inTokenizer.getStreamName() != "")
|
||
|
lStream << " in file \"" << inTokenizer.getStreamName() << "\",";
|
||
|
lStream << " at line ";
|
||
|
lStream << inTokenizer.getLineNumber();
|
||
|
switch(mType) {
|
||
|
case eCDATA: lStream << "\nfor CDATA \""; break;
|
||
|
case eComment: lStream << "\nfor comment \""; break;
|
||
|
case eData: lStream << "\nfor markup \""; break;
|
||
|
case ePI: lStream << "\nfor processing instruction \""; break;
|
||
|
case eRoot: lStream << "\nfor root element \""; break;
|
||
|
case eSpecial: lStream << "\nfor special element \""; break;
|
||
|
case eString: lStream << "\nfor literal string \""; break;
|
||
|
case eDecl: lStream << "\nfor declaration \""; break;
|
||
|
default: lStream << "\nfor unknown element \""; break;
|
||
|
}
|
||
|
if(getValue().size() < 40) lStream << getValue() << "\": " << inMessage;
|
||
|
else lStream << getValue().substr(0,40) << "...\": " << inMessage;
|
||
|
throw runtime_error(lStream.str());
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
*/
|
||
|
ostream& PACC::operator<<(ostream &outStream, const XML::Node& inNode)
|
||
|
{
|
||
|
XML::Streamer lStream(outStream);
|
||
|
inNode.serialize(lStream);
|
||
|
return outStream;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
*/
|
||
|
istream& PACC::operator>>(istream &inStream, XML::Node& outNode)
|
||
|
{
|
||
|
Tokenizer lTokenizer(inStream);
|
||
|
XML::Node* lNode = XML::Node::parse(lTokenizer, set<string>());
|
||
|
outNode = *lNode;
|
||
|
delete lNode;
|
||
|
return inStream;
|
||
|
}
|