972 lines
25 KiB
C++
972 lines
25 KiB
C++
//****************************************************************************
|
|
// Filename: XmlElements.h
|
|
// Copyright 1999 Daniel X. Pape. All rights reserved.
|
|
//
|
|
// Description: A set of classes for reading and parsing simple XML files.
|
|
//
|
|
//****************************************************************************
|
|
// Revision History:
|
|
// Thursday, July 08, 1999 - Original. Heavily based on "A Simple XML
|
|
// Parser" by Sebastien Andrivet. See Documentation.
|
|
//****************************************************************************
|
|
|
|
#include "XmlParser.h"
|
|
|
|
using std::string;
|
|
using std::auto_ptr;
|
|
|
|
|
|
// Macro to get the number of elements in a array
|
|
#ifndef elemof
|
|
#define elemof(array) (sizeof(array) / sizeof((array)[0]))
|
|
#endif
|
|
|
|
// Our namespace
|
|
namespace SimpleXMLParser
|
|
{
|
|
|
|
// ***************************************************************************
|
|
// Function: Constructor
|
|
// Desc:
|
|
// ***************************************************************************
|
|
XmlParser::XmlParser()
|
|
: source_(0), sourceCurrent_(0), sourceEnd_(0),
|
|
line_(1), column_(1), xmlVersion_("1.0")
|
|
{
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: SyntaxError
|
|
// Desc: Syntax error (throw exception)
|
|
// ***************************************************************************
|
|
void XmlParser::SyntaxError()
|
|
{
|
|
throw XmlException(line_, column_);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: NextChar
|
|
// Desc: Next char (next position)
|
|
// ***************************************************************************
|
|
char XmlParser::NextChar()
|
|
{
|
|
if(sourceCurrent_ >= sourceEnd_) return(0);
|
|
char c = *sourceCurrent_++;
|
|
|
|
// Skip \r if any
|
|
if(c == '\r')
|
|
{
|
|
if(sourceCurrent_ >= sourceEnd_) return(0);
|
|
c = *sourceCurrent_++;
|
|
}
|
|
|
|
if(c == '\n')
|
|
++line_, column_ = 1;
|
|
else
|
|
++column_;
|
|
|
|
return(c);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: PreviousChar
|
|
// Desc: Previous position
|
|
// ***************************************************************************
|
|
void XmlParser::PreviousChar()
|
|
{
|
|
if(sourceCurrent_ - 1 < source_)
|
|
sourceCurrent_ = source_;
|
|
else
|
|
sourceCurrent_ -= 1;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseString
|
|
// Desc:
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseString(const char* str)
|
|
{
|
|
Bookmark bookmark(*this);
|
|
|
|
// not end of string ?
|
|
while(*str != 0)
|
|
{
|
|
// Next char
|
|
char c = NextChar();
|
|
// Same ?
|
|
if(c != *str)
|
|
{
|
|
// Not the same so revert back to
|
|
// the previous position
|
|
bookmark.Restore();
|
|
return(false);
|
|
}
|
|
|
|
// Next char of the string
|
|
++str;
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseStringNoCase
|
|
// Desc: Read the given string (not case sensitive)
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseStringNoCase(const char* str)
|
|
{
|
|
Bookmark bookmark(*this);
|
|
|
|
// not end of string ?
|
|
while(*str != 0)
|
|
{
|
|
// Next char
|
|
char c = NextChar();
|
|
// Same (not case sensitive) ?
|
|
if(LowCase(c) != LowCase(*str))
|
|
{
|
|
// Not the same so revert back to
|
|
// the previous position
|
|
bookmark.Restore();
|
|
return(false);
|
|
}
|
|
|
|
// Next char of the string
|
|
++str;
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseNumber
|
|
// Desc: Read a (decimal) number
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseNumber(int& n)
|
|
{
|
|
char c = NextChar();
|
|
|
|
if(!IsDigit(c))
|
|
return(false); // not a number
|
|
|
|
n = 0;
|
|
while(IsDigit(c))
|
|
{
|
|
// Compute new number
|
|
n = n * 10 + c - '0';
|
|
// Next char
|
|
c = NextChar();
|
|
}
|
|
|
|
// The current char if not part of the number
|
|
PreviousChar();
|
|
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseHexNumber
|
|
// Desc: Read an hexadecimal number
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseHexNumber(int& n)
|
|
{
|
|
char c = NextChar();
|
|
|
|
if(!IsHexDigit(c))
|
|
return(false);
|
|
|
|
n = 0;
|
|
// Read all digits possible
|
|
while(IsHexDigit(c))
|
|
{
|
|
// Compute new number
|
|
n = n * 16 + HexDigitValue(c);
|
|
// Next char
|
|
c = NextChar();
|
|
}
|
|
|
|
// The current char if not part of the number
|
|
PreviousChar();
|
|
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseChar
|
|
// Desc:
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseChar(char c)
|
|
{
|
|
if(NextChar() != c)
|
|
{
|
|
PreviousChar();
|
|
return(false);
|
|
}
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseSpaces
|
|
// Desc: Read One or more spaces
|
|
// S ::= (#x20 | #x9 | #xD | #xA)+
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseSpaces()
|
|
{
|
|
char c = NextChar();
|
|
if(!IsSpace(c))
|
|
{
|
|
PreviousChar();
|
|
return(false);
|
|
}
|
|
|
|
do c = NextChar();
|
|
while(IsSpace(c));
|
|
|
|
PreviousChar();
|
|
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseDeclBegining
|
|
// Desc: Parse a declaration (like: version = )
|
|
// S <szString> Eq
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseDeclBegining(const char * szString)
|
|
{
|
|
// Parse: S
|
|
char c = NextChar();
|
|
if(!IsSpace(c))
|
|
{
|
|
PreviousChar();
|
|
return(false);
|
|
}
|
|
ParseSpaces();
|
|
|
|
// Parse: <szString>
|
|
if(!ParseString(szString))
|
|
return(false);
|
|
|
|
// Parse: Eq
|
|
if(!ParseEq())
|
|
SyntaxError();
|
|
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseXMLDecl
|
|
// Desc: Parse XML declaration
|
|
// XMLDecl ::= '<?xml' VersionInfo EncodingDecl? S? '?>'
|
|
// Marker: '<?xml'
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseXMLDecl()
|
|
{
|
|
// Parse: '<?xml'
|
|
if(!ParseStringNoCase("<?xml"))
|
|
return(false);
|
|
|
|
// Parse: VersionInfo EncodingDecl? SDDecl? S? '?>'
|
|
if(!ParseVersionInfo(xmlVersion_))
|
|
SyntaxError();
|
|
|
|
// Parse EncodingDecl (optional)
|
|
ParseEncodingDecl();
|
|
// Parse S (spaces) (optional)
|
|
ParseSpaces();
|
|
|
|
// Parse end of declaration '?>'
|
|
if(!ParseString("?>"))
|
|
SyntaxError();
|
|
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseEq
|
|
// Desc: Parse equal sign
|
|
// Eq ::= S? '=' S?
|
|
// Marker: '='
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseEq()
|
|
{
|
|
// Record the current position
|
|
Bookmark bookmark(*this);
|
|
|
|
// Parse spaces (optional)
|
|
ParseSpaces();
|
|
// Is an equal sign ?
|
|
if(!ParseChar('='))
|
|
{
|
|
// No, so revert to the previous position
|
|
bookmark.Restore();
|
|
return(false);
|
|
}
|
|
// Skip spaces if any
|
|
ParseSpaces();
|
|
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseVersionInfo
|
|
// Desc: Parse XML version
|
|
// VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ")
|
|
// Marker: 'version'
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseVersionInfo(string& version)
|
|
{
|
|
// Parse: S 'version' Eq
|
|
if(!ParseDeclBegining("version"))
|
|
return(false);
|
|
|
|
// Parse: (' VersionNum ' | " VersionNum ")
|
|
char c = NextChar();
|
|
if(c != '\'' && c != '\"')
|
|
SyntaxError();
|
|
|
|
// Parse version number and check the delimiter
|
|
if(!ParseVersionNum(version) || NextChar() != c)
|
|
SyntaxError();
|
|
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseVersionNum
|
|
// Desc: Parse XML version number
|
|
// VersionNum ::= ([a-zA-Z0-9_.:] | '-')+
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseVersionNum(string& version)
|
|
{
|
|
// Record the current position
|
|
Bookmark bookmark(*this);
|
|
|
|
char c = NextChar();
|
|
// Is an allowed character ?
|
|
if(!IsAlphaDigitEx(c))
|
|
return(false);
|
|
|
|
c = NextChar();
|
|
// Get as more char as possible
|
|
while(IsAlphaDigitEx(c))
|
|
c = NextChar();
|
|
|
|
// Current character is not part of the version num.
|
|
PreviousChar();
|
|
|
|
// Get the version number
|
|
bookmark.GetSubString(version);
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseEncodingDecl
|
|
// Desc: Parse XML encoding declaration
|
|
// EncodingDecl ::= S 'encoding' Eq
|
|
// ('"' EncName '"' | "'" EncName "'")
|
|
// Marker: 'encoding'
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseEncodingDecl()
|
|
{
|
|
// Parse: S 'encoding' Eq
|
|
if(!ParseDeclBegining("encoding"))
|
|
return(false);
|
|
|
|
// Parse: ('"' EncName '"' | "'" EncName "'")
|
|
char c = NextChar();
|
|
if(c != '\'' && c != '\"')
|
|
SyntaxError();
|
|
|
|
// Parse encoding name and check delimiter
|
|
// Bug#0002: Was ParseEncName() instead of !ParseEncName()
|
|
if(!ParseEncName() || NextChar() != c)
|
|
SyntaxError();
|
|
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseEncName
|
|
// Desc: Parse encoding name
|
|
// EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseEncName()
|
|
{
|
|
char c = NextChar();
|
|
// Is an allowed character ?
|
|
if(!IsAlpha(c))
|
|
return(false);
|
|
|
|
c = NextChar();
|
|
// Get as more char as possible
|
|
while(IsAlphaDigitEx(c))
|
|
c = NextChar();
|
|
|
|
// Current character is not part of the version num.
|
|
PreviousChar();
|
|
|
|
// In this version, the encoding is not used
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseMiscs
|
|
// Desc: Parse Comments, spaces, etc.
|
|
// Misc*
|
|
// Misc ::= Comment | S
|
|
// ***************************************************************************
|
|
void XmlParser::ParseMiscs()
|
|
{
|
|
for(;;)
|
|
{
|
|
// Parse spaces if any
|
|
ParseSpaces();
|
|
|
|
// Parse comment if any
|
|
auto_ptr<ElementComment> pElem(ParseComment());
|
|
if(0 == pElem.get())
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseComment
|
|
// Desc: Parse comment and construct an element
|
|
// Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
|
|
// ***************************************************************************
|
|
ElementComment* XmlParser::ParseComment()
|
|
{
|
|
// Check the start of the comment
|
|
if(!ParseString("<!--"))
|
|
return(0);
|
|
|
|
// Record the current position to extract later the
|
|
// content of the comment
|
|
Bookmark bookmark(*this);
|
|
for(;;)
|
|
{
|
|
// Look for the end of the comment
|
|
if(ParseString("--"))
|
|
{
|
|
// Really the end ?
|
|
if(!ParseChar('>'))
|
|
SyntaxError();
|
|
break;
|
|
}
|
|
|
|
// End of document ?
|
|
if(NextChar() == 0)
|
|
SyntaxError();
|
|
}
|
|
|
|
// Extract the content of the comment
|
|
string strComment;
|
|
bookmark.GetSubString(strComment, 3);
|
|
|
|
// Construct an element
|
|
return(new ElementComment(strComment));
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseName
|
|
// Desc: Read a name (letters, digits and special chars)
|
|
// Name ::= (Letter | '_' | ':') (NameChar)*
|
|
// NameChar ::= Letter | Digit | '.' | '-' |
|
|
// '_' | ':' | CombiningChar | Extender
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseName(string& name)
|
|
{
|
|
// Record the current position to extract later the name
|
|
Bookmark bookmark(*this);
|
|
|
|
char c = NextChar();
|
|
// Is allowed ?
|
|
if(!IsAlpha(c) && c != '_' && c != ':')
|
|
{
|
|
PreviousChar();
|
|
return(false);
|
|
}
|
|
|
|
// Get as more (allowed) char as possible
|
|
for(;;)
|
|
{
|
|
c = NextChar();
|
|
// Is allowed ?
|
|
if(!IsAlphaDigitEx(c))
|
|
break;
|
|
}
|
|
|
|
// Current character is not part of the version num.
|
|
PreviousChar();
|
|
|
|
// Extract the name
|
|
bookmark.GetSubString(name);
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseReference
|
|
// Desc: Parse reference
|
|
// Reference ::= EntityRef | CharRef
|
|
// EntityRef ::= '&' Name ';'
|
|
// CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
|
|
// Marker: '&'
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseReference(char& cRef)
|
|
{
|
|
// Begin like a reference ?
|
|
if(!ParseChar('&'))
|
|
return(false);
|
|
|
|
char c = NextChar();
|
|
// EntityRef ? (not a CharRef)
|
|
if(c != '#')
|
|
{
|
|
// It is an EntityRef
|
|
PreviousChar();
|
|
// Get the name of the reference and check the end (';')
|
|
string strReferenceName;
|
|
if(!ParseName(strReferenceName) || !ParseChar(';'))
|
|
SyntaxError();
|
|
// Look for the reference
|
|
if(!MapReferenceName(strReferenceName, cRef))
|
|
SyntaxError();
|
|
|
|
return(true);
|
|
}
|
|
|
|
// It is a CharRef
|
|
c = NextChar();
|
|
// Compute the value (character code)
|
|
int n = 0;
|
|
// Hexadecimal ?
|
|
if(c == 'x')
|
|
{
|
|
// Get the value
|
|
if(!ParseHexNumber(n))
|
|
SyntaxError();
|
|
}
|
|
else
|
|
{
|
|
PreviousChar();
|
|
// Get the value
|
|
if(!ParseNumber(n))
|
|
SyntaxError();
|
|
}
|
|
|
|
// Check the end of the reference
|
|
if(!ParseChar(';'))
|
|
SyntaxError();
|
|
|
|
// Return the character with the computed code
|
|
cRef = static_cast<char>(n);
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseAttValue
|
|
// Desc: Parse attribute value
|
|
// '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'"
|
|
// Marker: '"' | "'"
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseAttValue(string& value)
|
|
{
|
|
// Get the value delimiter (quote or apostrophe)
|
|
char cDelim = NextChar();
|
|
if(cDelim != '\'' && cDelim != '\"')
|
|
{
|
|
PreviousChar();
|
|
return(false);
|
|
}
|
|
|
|
// Record the current position to extract later the value
|
|
Bookmark bookmark(*this);
|
|
|
|
char c = NextChar();
|
|
// Search the end of the value
|
|
while(c != cDelim)
|
|
{
|
|
switch(c)
|
|
{
|
|
case 0: // end of document
|
|
SyntaxError();
|
|
|
|
case '<': // Tag
|
|
SyntaxError();
|
|
|
|
case '&': // Reference
|
|
{
|
|
PreviousChar();
|
|
// Put what we already have in the value
|
|
string before;
|
|
bookmark.GetSubString(before);
|
|
value += before;
|
|
|
|
char c;
|
|
// Get the reference
|
|
if(!ParseReference(c))
|
|
SyntaxError();
|
|
|
|
// Put the char in the value
|
|
value += c;
|
|
// Record the new position (after the reference)
|
|
bookmark.Reset();
|
|
}
|
|
//break;
|
|
// fall-though intentional -- DXP
|
|
default:
|
|
// Next character
|
|
c = NextChar();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Put the remaining of the value
|
|
string remaining;
|
|
bookmark.GetSubString(remaining, 1);
|
|
value += remaining;
|
|
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseAttribute
|
|
// Desc: Parse Attribute
|
|
// Attribute ::= Name Eq AttValue
|
|
// Marker: Name
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseAttribute(ElementTag* elem)
|
|
{
|
|
// Get attribute name
|
|
string name;
|
|
if(!ParseName(name))
|
|
return(false);
|
|
|
|
// Get attribute value after the equal sign
|
|
string value;
|
|
if(!ParseEq() || !ParseAttValue(value))
|
|
SyntaxError();
|
|
|
|
// Construct an Attribute object and add it to the element
|
|
elem->AddAttribute(name, value);
|
|
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseCDATA
|
|
// Desc: Parse CDATA
|
|
// CDSect ::= '<![CDATA[' CData ']]>'
|
|
// CData ::= (Char* - (Char* ']]>' Char*))
|
|
// Marker: '<![CDATA['
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseCDATA(Element& elem)
|
|
{
|
|
// Parse: <![CDATA[
|
|
if(!ParseString("<![CDATA["))
|
|
return(false);
|
|
|
|
Bookmark bookmark(*this);
|
|
// Parse: CData
|
|
for(;;)
|
|
{
|
|
// End of CDATA ?
|
|
if(ParseString("]]>"))
|
|
break;
|
|
|
|
// Is character allowed ?
|
|
if(!IsXmlChar(NextChar()))
|
|
SyntaxError();
|
|
}
|
|
|
|
// Get CDATA content and add it as-is to the value
|
|
string strCDATA;
|
|
bookmark.GetSubString(strCDATA, 3);
|
|
elem.AddValue(strCDATA);
|
|
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseTagBegining
|
|
// Desc: Parse start tag and construct an element
|
|
// '<' Name (S Attribute)* S?
|
|
// Marker: '<'
|
|
// ***************************************************************************
|
|
ElementTag* XmlParser::ParseTagBegining()
|
|
{
|
|
// Parse: '<'
|
|
if(!ParseChar('<'))
|
|
return(0);
|
|
|
|
// Get the name of the tag
|
|
string strName;
|
|
if(!ParseName(strName))
|
|
SyntaxError();
|
|
|
|
// Construct an element object
|
|
auto_ptr<ElementTag> pElem(new ElementTag(strName));
|
|
|
|
// Parse: (S Attribute)* S?
|
|
while(ParseSpaces() && ParseAttribute(pElem.get()))
|
|
;
|
|
|
|
// return the element to the caller
|
|
return(pElem.release());
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseETag
|
|
// Desc: Parse end tag
|
|
// ETag ::= '</' Name S? '>'
|
|
// Marker: '</'
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseETag(Element& elem)
|
|
{
|
|
// Is an End tag ?
|
|
if(!ParseString("</"))
|
|
return(false);
|
|
|
|
// Get the tag name
|
|
string endTagName;
|
|
if(!ParseName(endTagName))
|
|
SyntaxError();
|
|
|
|
// Start and end tag names must match
|
|
if(endTagName != elem.GetName())
|
|
SyntaxError();
|
|
|
|
// Skip spaces
|
|
ParseSpaces();
|
|
// End of the tag
|
|
if(!ParseChar('>'))
|
|
SyntaxError();
|
|
|
|
return(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseMarkup
|
|
// Desc: Parse markups like comments, CDATA, elements, etc.
|
|
// Comment begins with '<!--'
|
|
// CDSect begins with '<![CDATA['
|
|
// element begins with '<'
|
|
// ***************************************************************************
|
|
bool XmlParser::ParseMarkup(Element& elem)
|
|
{
|
|
// Try to read a comment
|
|
auto_ptr<ElementComment> comment(ParseComment());
|
|
if(0 != comment.get())
|
|
{
|
|
// Add it to the element
|
|
if(!elem.AddChild(comment.release()))
|
|
SyntaxError();
|
|
return(true);
|
|
}
|
|
|
|
// Try to read a CDATA
|
|
if(ParseCDATA(elem))
|
|
return(true);
|
|
|
|
// Try to read an element
|
|
auto_ptr<ElementTag> tag(ParseElement());
|
|
if(0 != tag.get())
|
|
{
|
|
if(!elem.AddChild(tag.release()))
|
|
SyntaxError();
|
|
return(true);
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseContentETag
|
|
// Desc: Parse element content and end tag
|
|
// content ::= (element | CharData | Reference | CDSect | Comment)*
|
|
// CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
|
|
// Reference begins with '&'
|
|
// ***************************************************************************
|
|
void XmlParser::ParseContentETag(ElementTag& elem)
|
|
{
|
|
// Record the current position to extract later the content
|
|
Bookmark bookmark(*this);
|
|
|
|
char c = NextChar();
|
|
for(;;)
|
|
{
|
|
// "]]>" not allowed in content
|
|
if(ParseString("]]>"))
|
|
SyntaxError();
|
|
|
|
switch(c)
|
|
{
|
|
case 0: // End of document
|
|
SyntaxError();
|
|
break;
|
|
|
|
case '&':
|
|
case '<':
|
|
{
|
|
PreviousChar();
|
|
|
|
// Put what we already have in the value
|
|
string value;
|
|
bookmark.GetSubString(value);
|
|
elem.AddValue(value);
|
|
|
|
// Tag or reference ?
|
|
if(c == '&')
|
|
{
|
|
// Get the reference
|
|
if(!ParseReference(c))
|
|
SyntaxError();
|
|
|
|
// Add it to the value
|
|
elem.AddValue(c);
|
|
}
|
|
else
|
|
{
|
|
// Is an end tag ?
|
|
if(ParseETag(elem))
|
|
return;
|
|
else if(!ParseMarkup(elem))
|
|
SyntaxError();
|
|
}
|
|
|
|
bookmark.Reset();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Next char
|
|
c = NextChar();
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseElement
|
|
// Desc: Parse element and construct an object
|
|
// element ::= EmptyElemTag | STag content ETag
|
|
// EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
|
|
// STag ::= '<' Name (S Attribute)* S? '>'
|
|
// Marker: '<'
|
|
// ***************************************************************************
|
|
ElementTag* XmlParser::ParseElement()
|
|
{
|
|
// Begining of element (start tag)
|
|
auto_ptr<ElementTag> tag(ParseTagBegining());
|
|
if(tag.get() == 0)
|
|
return(0);
|
|
|
|
char c = NextChar();
|
|
if(c == '/') // Empty tag ?
|
|
{
|
|
c = NextChar();
|
|
if(c != '>')
|
|
SyntaxError();
|
|
|
|
return(tag.release());
|
|
}
|
|
|
|
// End of the tag
|
|
if(c != '>')
|
|
SyntaxError();
|
|
|
|
// Parse the remaining of the element
|
|
ParseContentETag(*tag);
|
|
return(tag.release());
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: ParseDocument
|
|
// Desc: Parse document and construct a (root) element
|
|
// document ::= XMLDecl? Misc* element Misc*
|
|
// ***************************************************************************
|
|
Element* XmlParser::ParseDocument()
|
|
{
|
|
// XML declaration (optional)
|
|
ParseXMLDecl();
|
|
ParseMiscs();
|
|
|
|
// Get the root element
|
|
auto_ptr<Element> elem(ParseElement());
|
|
if(0 == elem.get())
|
|
SyntaxError();
|
|
|
|
ParseMiscs();
|
|
|
|
return(elem.release());
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// Function: Parse
|
|
// Desc: parse document
|
|
// ***************************************************************************
|
|
Element& XmlParser::Parse(const char * szSource, int nSourceSize)
|
|
{
|
|
source_ = szSource;
|
|
sourceCurrent_ = source_;
|
|
sourceEnd_ = source_ + nSourceSize;
|
|
line_ = column_ = 1;
|
|
|
|
// Get the root eleement
|
|
#ifdef _MSC_VER
|
|
rootElem_ = std::auto_ptr<Element>(ParseDocument());
|
|
#else
|
|
rootElem_.reset(ParseDocument());
|
|
#endif
|
|
|
|
return(*rootElem_);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Entity references
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
// Map a name to a character
|
|
struct MapReference
|
|
{
|
|
const char * szName;
|
|
char c;
|
|
};
|
|
|
|
// Predefined refererences
|
|
static const MapReference s_MapReference[] =
|
|
{
|
|
{ "lt", '<' },
|
|
{ "gt", '>' },
|
|
{ "amp", '&' },
|
|
// { "apos", '\"' },
|
|
// { "quot", '\'' } DXP
|
|
{ "apos", '\'' },
|
|
{ "quot", '\"' }
|
|
};
|
|
|
|
// ***************************************************************************
|
|
// Function: MapReferenceName
|
|
// Desc: Find the reference strName and return its equivalent
|
|
// ***************************************************************************
|
|
bool XmlParser::MapReferenceName(const string& strName, char& c)
|
|
{
|
|
for(unsigned int nIndex = 0; nIndex < elemof(s_MapReference); ++nIndex)
|
|
{
|
|
// Same name ?
|
|
if(strName == s_MapReference[nIndex].szName)
|
|
{
|
|
// return the equivalent
|
|
c = s_MapReference[nIndex].c;
|
|
return(true);
|
|
}
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
}
|
|
|