first commit
This commit is contained in:
@@ -0,0 +1,971 @@
|
||||
//****************************************************************************
|
||||
// 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user