// XMLParser.cpp: implementation of the WMXMLParser class.
//
//////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2003 Robert Cunnings. All rights reserved.
//
// You may copy, modify, distribute or publish this code free of charge.
//
// NO WARRANTY: This software is provided AS IS, without warranty of any kind.
//
//////////////////////////////////////////////////////////////////////
//
// XMLParser class - a no frills XML parser that generates SAX like events
// while parsing. Event handlers must inherit from class WMXMLEventHandler
// (defined in XMLParser.h). Input is accepted in UTF8 or UTF16 encoding.
// The parser converts UTF8 to UNICODE for the purpose of parsing the document.
// UTF16 input must have the proper BOM at the start. Namespaces are supported.
//  
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "windows.h"
#include "charencode.h"
#include "XMLParser.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

WMXMLParser::WMXMLParser()
{
	m_pwszStart = NULL;
	m_nLineNbr = 1;
	m_pEventHandler = NULL;
	m_pDocRoot = NULL;
	m_bBuildTree = true;
	// init reference map
	m_mapReferences.insert(map<wstring, wchar_t>::value_type(wstring(L"lt"), wchar_t('<')));
	m_mapReferences.insert(map<wstring, wchar_t>::value_type(wstring(L"gt"), wchar_t('>')));
	m_mapReferences.insert(map<wstring, wchar_t>::value_type(wstring(L"amp"), wchar_t('&')));
	m_mapReferences.insert(map<wstring, wchar_t>::value_type(wstring(L"apos"), wchar_t('\'')));
	m_mapReferences.insert(map<wstring, wchar_t>::value_type(wstring(L"quot"), wchar_t('\"')));
}

WMXMLParser::~WMXMLParser()
{
	if (m_pDocRoot != NULL)
	{
		delete m_pDocRoot;
	}
}

void WMXMLParser::SaveLocation(LOCATION& Loc)
{
	Loc.pwszLocation = m_pwszCurrent;
	Loc.nLineNbr = m_nLineNbr;
}

void WMXMLParser::RestoreLocation(LOCATION& Loc)
{
	m_pwszCurrent = Loc.pwszLocation;
	m_nLineNbr = Loc.nLineNbr;
}

wchar_t WMXMLParser::NextCh()
{
	if (m_pwszCurrent >= m_pwszStop)
		return 0;
	wchar_t c = *m_pwszCurrent++;
	// line counting
	if (c == 0x000A)
	{
		m_nLineNbr++;
	}
	return c;
}

void WMXMLParser::PrevCh()
{
	m_pwszCurrent--;
	if (m_pwszCurrent < m_pwszStart)
		m_pwszCurrent = m_pwszStart;
}

bool WMXMLParser::ParseWhiteSpace()
{
	wchar_t c = NextCh();
	if (IsWhiteSpace(c) == false)
	{
		PrevCh();
		return false;
	}
	do
	{
		c = NextCh();
	} while (IsWhiteSpace(c) == true);
	PrevCh();
	return true;
}

bool WMXMLParser::ParseCh(wchar_t c)
{
	if (NextCh() != c)
	{
		PrevCh();
		return false;
	}
	return true;
}

bool WMXMLParser::ParseString(const wchar_t *pwszString)
{
	LOCATION Loc;
	SaveLocation(Loc);
	while (*pwszString)
	{
		wchar_t c = NextCh();
		if (c != *pwszString++)
		{
			RestoreLocation(Loc);
			return false;
		}
	}
	return true;
}

bool WMXMLParser::ParseDecNumber(int& nNbr)
{
	wchar_t c = NextCh();
	if (IsDigit(c) == false)
		return false;
	nNbr = 0;
	do
	{
		wchar_t wszBuffer[2];
		wszBuffer[0] = c;
		wszBuffer[1] = 0;
		int nTemp = _wtoi(wszBuffer);
		nNbr = (nNbr * 10) + DecDigitValue(c);
		c = NextCh();
	} while (IsDigit(c) == true);
	PrevCh();
	return true;
}

bool WMXMLParser::ParseHexNumber(int& nNbr)
{
	wchar_t c = NextCh();
	if (IsHexDigit(c) == false)
		return false;
	nNbr = 0;
	do
	{
		nNbr = (nNbr * 16) + HexDigitValue(c);
		c = NextCh();
	} while (IsHexDigit(c) == true);
	PrevCh();
	return true;
}

bool WMXMLParser::ParseStringInsensitive(const wchar_t *pwszString)
{
	LOCATION Loc;
	SaveLocation(Loc);
	while (*pwszString)
	{
		wchar_t c = NextCh();
		if (_totupper((wint_t)c) != _totupper((wint_t)*pwszString++))
		{
			RestoreLocation(Loc);
			return false;
		}
	}
	return true;
}

bool WMXMLParser::ParseDocTypeDecl(wstring& wstrName, wstring& wstrContent)
{
	if (ParseString(L"<!DOCTYPE") == false)
		return false;
	ParseWhiteSpace();
	if (ParseName(wstrName) == false)
		return false;
	ParseWhiteSpace();
	LOCATION Loc;
	SaveLocation(Loc);
	while (true)
	{
		if (ParseString(L"<!") == true)
		{
			while (true)
			{
				if (ParseCh(wchar_t('>')) == true)
					break;
				wchar_t c = NextCh();
				if (c == 0)
					Error();
			}
		}
		if (ParseCh(wchar_t('>')) == true)
			break;
		wchar_t c = NextCh();
		if (c == 0)
			Error();
	}
	wstrContent += wstring(Loc.pwszLocation, m_pwszCurrent - Loc.pwszLocation - 1);
	return true;
}

bool WMXMLParser::ParsePI(wstring& wstrTarget, wstring& wstrContent)
{
	if (ParseString(L"<?") == false)
		return false;
	if (ParseName(wstrTarget) == false)
		return false;
	ParseWhiteSpace();
	LOCATION Loc;
	SaveLocation(Loc);
	while (true)
	{
		if (ParseString(L"?>") == true)
			break;
		wchar_t c = NextCh();
		if (c == 0)
			Error();
	}
	wstrContent += wstring(Loc.pwszLocation, m_pwszCurrent - Loc.pwszLocation - 2);
	FirePIEvent(wstrTarget, wstrContent);
	return true;
}

bool WMXMLParser::ParseComment(wstring& wstrContent)
{
	if (ParseString(L"<!--") == false)
		return false;
	LOCATION Loc;
	SaveLocation(Loc);
	while (true)
	{
		if (ParseString(L"--") == true)
		{
			if (ParseCh(wchar_t('>')) == false)
				Error();
			break;
		}
		wchar_t c = NextCh();
		if (c == 0)
			Error();
	}
	wstrContent += wstring(Loc.pwszLocation, m_pwszCurrent - Loc.pwszLocation - 3);
	return true;
}

bool WMXMLParser::ParseName(wstring& wstrName)
{
	LOCATION Loc;
	SaveLocation(Loc);
	wchar_t c = NextCh();
	if (IsAlpha(c) == false && c != wchar_t('_') && c != wchar_t(':'))
	{
		PrevCh();
		return false;
	}
	do
	{
		c = NextCh();
	} while (IsAlphaDigitEx(c) == true);
	PrevCh();
	wstrName = wstring(Loc.pwszLocation, m_pwszCurrent - Loc.pwszLocation);
	return true;
}

bool WMXMLParser::ParseCDATA(WMXMLElement *pElement)
{
	if (ParseString(L"<![CDATA[") == false)
		return false;
	LOCATION Loc;
	SaveLocation(Loc);
	while (true)
	{
		if (ParseString(L"]]>") == true)
			break;
		wchar_t c = NextCh();
		if (IsXMLCharacter(c) == false)
			Error();
	}
	pElement->AppendContent(wstring(Loc.pwszLocation, m_pwszCurrent - Loc.pwszLocation - 3));
	return true;
}

bool WMXMLParser::ParseMarkup(WMXMLElement *pElement)
{
	wstring wstrName;
	wstring wstrContent;
	if (ParseComment(wstrContent) == true)
		return true;
	if (ParsePI(wstrName, wstrContent) == true)
		return true;
	if (ParseCDATA(pElement) == true)
		return true;
	if (ParseElement(pElement) == NULL)
		Error();
	return true;
}

bool WMXMLParser::ParseEndTag(WMXMLElement *pElement)
{
	if (ParseString(L"</") == false)
		return false;
	wstring wstrEndName;
	if (ParseName(wstrEndName) == false)
		Error();
	if (wstrEndName != pElement->GetQualifiedName())
		Error();
	ParseWhiteSpace();
	if (ParseCh(wchar_t('>')) == false)
		Error();
	return true;
}

bool WMXMLParser::ParseContent(WMXMLElement *pElement)
{
	LOCATION Loc;
	SaveLocation(Loc);
	wchar_t c = NextCh();
	while (true)
	{
		if (c == 0 || ParseString(L"]]>") == true)
			Error();
		else if (c == wchar_t('&'))
		{
			PrevCh();
			pElement->AppendContent(wstring(Loc.pwszLocation, m_pwszCurrent - Loc.pwszLocation));
			wchar_t r;
			if (ParseReference(r) == false)
				Error();
			pElement->AppendContent(r);
			SaveLocation(Loc);
		}
		else if (c == wchar_t('<'))
		{
			PrevCh();
			pElement->AppendContent(wstring(Loc.pwszLocation, m_pwszCurrent - Loc.pwszLocation));
			if (ParseEndTag(pElement) == true)
				return true;
			else if (ParseMarkup(pElement) == false)
				Error();
			SaveLocation(Loc);
		}
		c = NextCh();
	}
	return true;
}

bool WMXMLParser::Dereference(const wstring& wstrRef, wchar_t& r)
{
	map<wstring, wchar_t>::iterator iter;
	iter = m_mapReferences.find(wstrRef);
	if (iter != m_mapReferences.end())
	{
		r = (*iter).second;
		return true;
	}
	return false;
}

bool WMXMLParser::ParseReference(wchar_t& r)
{
	if (ParseCh(wchar_t('&')) == false)
		return false;
	wchar_t c = NextCh();
	if (c != wchar_t('#'))
	{
		PrevCh();
		wstring wstrRefName;
		if (ParseName(wstrRefName) == false || ParseCh(wchar_t(';')) == false)
			Error();
		if (Dereference(wstrRefName, r) == false)
			Error();
		return true;
	}
	c = NextCh();
	int nNbr = 0;
	if (c == wchar_t('x'))
	{
		if (ParseHexNumber(nNbr) == false)
			Error();
	}
	else
	{
		PrevCh();
		if (ParseDecNumber(nNbr) == false)
			Error();
	}
	if (ParseCh(wchar_t(';')) == false)
		Error();
	r = static_cast<wchar_t>(nNbr);
	return true;
}

bool WMXMLParser::ParseAttributeValue(wstring& wstrValue)
{
	wchar_t q = NextCh();
	if (q != wchar_t('\'') && q != wchar_t('\"'))
	{
		PrevCh();
		return false;
	}
	LOCATION Loc;
	SaveLocation(Loc);
	wchar_t c = NextCh();
	while (c != q)
	{
		if (c == 0 || c == wchar_t('<'))
			Error();
		if (c == wchar_t('&'))
		{
			PrevCh();
			wstrValue += wstring(Loc.pwszLocation, m_pwszCurrent - Loc.pwszLocation);
			wchar_t r;
			if (ParseReference(r) == false)
				Error();
			wstrValue += r;
			SaveLocation(Loc);
		}
		c = NextCh();
	}
	wstrValue += wstring(Loc.pwszLocation, m_pwszCurrent - Loc.pwszLocation - 1);

	return true;
}

bool WMXMLParser::ParseAttribute(WMXMLElement *pElement)
{
	wstring wstrName;
	if (ParseName(wstrName) == false)
		return false;
	ParseWhiteSpace();
	if (ParseCh(wchar_t('=')) == false)
		Error();
	ParseWhiteSpace();
	wstring wstrValue;
	if (ParseAttributeValue(wstrValue) == false ||
				pElement->SetAttribute(wstrName, wstrValue) == false)
		Error();
	return true;
}

WMXMLElement *WMXMLParser::ParseElementStart(WMXMLElement *pParent)
{
	ParseWhiteSpace();
	if (ParseCh(wchar_t('<')) == false)
		return NULL;
	wstring wstrQualName;
	if (ParseName(wstrQualName) == false)
		Error();
	WMXMLElement *pElement = new WMXMLElement(wstrQualName);
    if (pParent != NULL)
        pParent->AddChildElement(pElement);
    else
        m_pDocRoot = pElement;
	do
	{
		ParseWhiteSpace();
	} while (ParseAttribute(pElement) == true);
	return pElement;
}

WMXMLElement *WMXMLParser::ParseElement(WMXMLElement *pParent)
{
	WMXMLElement *pElement;
	pElement = ParseElementStart(pParent);
	if (pElement == NULL)
		return NULL;
	if (pElement->ResolveNSPrefixes(pParent) == false)
		Error();
	FireStartNSMappingEvents(pElement, pParent);
    // see if this element is empty
	wchar_t c = NextCh();
	if (c == wchar_t('/'))
	{
		c = NextCh();
		if (c != wchar_t('>'))
			Error();
		FireStartElementEvent(pElement);
		FireEndElementEvent(pElement);
		FireEndNSMappingEvents(pElement, pParent);
		return pElement;
	}
	if (c != wchar_t('>'))
		Error();
	FireStartElementEvent(pElement);
    // this may recurse
	ParseContent(pElement);
    // was character content found?
	if (pElement->GetContent().empty() == false)
		FireCharactersEvent(pElement);
	FireEndElementEvent(pElement);
	FireEndNSMappingEvents(pElement, pParent);
	return pElement;
}

void WMXMLParser::ParseMisc()
{
	wstring wstrContent;
	wstring wstrName;
	while (true)
	{
		ParseWhiteSpace();
		if (ParseComment(wstrContent) == false && ParsePI(wstrName, wstrContent) == false)
		{
			break;
		}
		wstrContent = L"";
		wstrName = L"";
	}
}

bool WMXMLParser::ParseProlog()
{
	wstring wstrContent;
	wstring wstrName;
	ParseMisc();
	while (ParseDocTypeDecl(wstrName, wstrContent) == true)
	{
		ParseMisc();
	}
	return true;
}

void WMXMLParser::ParseXMLDocument()
{
	if (m_pDocRoot != NULL)
	{
		delete m_pDocRoot;
        m_pDocRoot = NULL;
	}
	FireStartDocEvent();
	ParseProlog();
	m_pDocRoot = ParseElement(NULL);
	if (m_pDocRoot == NULL)
		Error();
	ParseMisc();
	FireEndDocEvent();
}

void WMXMLParser::ParseXML_UTF16_BE(const wstring& wstrBuffer, WMXMLEventHandler *pEventHandler)
{
	m_nLineNbr = 1;
	m_pwszStart = m_pwszCurrent = wstrBuffer.c_str();
	m_pwszStop = m_pwszStart + wstrBuffer.length();
	// look for byte order mark 0xFEFF (big endian) to indicate UTF-16 stream
	wchar_t wchBOM = NextCh();
	if (wchBOM != 0xFEFF)
		Error();
	m_pEventHandler = pEventHandler;
	ParseXMLDocument();
}

void WMXMLParser::ParseXML_UTF16_LE(const wstring& wstrBuffer, WMXMLEventHandler *pEventHandler)
{
	m_nLineNbr = 1;
	m_pwszStart = m_pwszCurrent = wstrBuffer.c_str();
	m_pwszStop = m_pwszStart + wstrBuffer.length();
	// look for byte order mark 0xFFFE (little endian) to indicate UTF-16 stream
	wchar_t wchBOM = NextCh();
	if (wchBOM != 0xFFFE)
		Error();
	m_pEventHandler = pEventHandler;
	ParseXMLDocument();
}

void WMXMLParser::ParseXML_WideString(const wstring& wstrBuffer, WMXMLEventHandler *pEventHandler)
{
	m_nLineNbr = 1;
	m_pwszStart = m_pwszCurrent = wstrBuffer.c_str();
	m_pwszStop = m_pwszStart + wstrBuffer.length();
	m_pEventHandler = pEventHandler;
	ParseXMLDocument();
}

void WMXMLParser::ParseXML_WideString(const BSTR bstrBuffer, WMXMLEventHandler *pEventHandler)
{
	m_nLineNbr = 1;
	m_pwszStart = m_pwszCurrent = bstrBuffer;
	m_pwszStop = m_pwszStart + SysStringLen(bstrBuffer);
	m_pEventHandler = pEventHandler;
	ParseXMLDocument();
}

void WMXMLParser::ParseXML_UTF8(const string& strBuffer, WMXMLEventHandler *pEventHandler)
{
	wstring wstrXML;
	UTF8ToWideString(strBuffer, wstrXML);
	m_nLineNbr = 1;
	m_pwszStart = m_pwszCurrent = wstrXML.c_str();
	m_pwszStop = m_pwszStart + wstrXML.length();
	m_pEventHandler = pEventHandler;
	ParseXMLDocument();
}

void WMXMLParser::ParseXML(const char *pszBuffer, unsigned long ulSize, WMXMLEventHandler *pEventHandler)
{
	wstring wstrXML;
	m_nLineNbr = 1;
	// look for BOM indicating UTF-16 encoding
	if ((unsigned char)pszBuffer[0] == 0xff && (unsigned char)pszBuffer[1] == 0xfe)
	{
		// UTF-16 BOM == 0xFFFE (little endian)
		m_pwszStart = m_pwszCurrent = (wchar_t *)pszBuffer;
		m_pwszStop = m_pwszStart + (ulSize / 2);
		// consume byte order mark
		wchar_t wchBOM = NextCh();
	}
	else if ((unsigned char)pszBuffer[0] == 0xfe && (unsigned char)pszBuffer[1] == 0xff)
	{
		// UTF-16 BOM == 0xFEFF (big endian)
		unsigned long ulLength = ulSize / 2;
		wstrXML.append((wchar_t *)pszBuffer, ulLength);
		// reverse endian
		for (unsigned long i = 0; i < ulLength; i++)
		{
			wchar_t wc, wcHi, wcLo;
			wc = wstrXML[i];
			// make low byte
			wcLo = wc >> 8;
			// make high byte
			wcHi = wc << 8;
			// reverse endian
			wstrXML[i] = wcLo | wcHi;
		}
		m_pwszStart = m_pwszCurrent = wstrXML.c_str();
		m_pwszStop = m_pwszStart + ulLength;
		// consume byte order mark
		wchar_t wchBOM = NextCh();
	}
	else if ((unsigned char)pszBuffer[0] == 0xef &&
				(unsigned char)pszBuffer[1] == 0xbb &&
				(unsigned char)pszBuffer[2] == 0xbf)
	{
		// UTF-8 with BOM encoded as EF,BB,BF
		UTF8ToWideString(pszBuffer + 3, ulSize, wstrXML);
		m_pwszStart = m_pwszCurrent = wstrXML.c_str();
		m_pwszStop = m_pwszStart + wstrXML.length();
	}
	else
	{
		// US ASCII or UTF-8 without encoded BOM at start
		UTF8ToWideString(pszBuffer, ulSize, wstrXML);
		m_pwszStart = m_pwszCurrent = wstrXML.c_str();
		m_pwszStop = m_pwszStart + wstrXML.length();
	}
	// now parse
	m_pEventHandler = pEventHandler;
	ParseXMLDocument();
}

void WMXMLParser::Error()
{
	FireFatalErrorEvent();
	if (m_pDocRoot != NULL)
		delete m_pDocRoot;
	throw m_nLineNbr;
}

void WMXMLParser::SetDocRoot(WMXMLElement *pRoot)
{
	if (m_pDocRoot != NULL)
		delete m_pDocRoot;
	m_pDocRoot = pRoot;
}

WMXMLElement *WMXMLParser::GetDocRoot()
{
	return m_pDocRoot;
}

WMXMLElement *WMXMLParser::DetachDocRoot()
{
	WMXMLElement *pTemp = m_pDocRoot;
	m_pDocRoot = NULL;
	return pTemp;
}

int WMXMLParser::SerializeXML_UTF8(string& strUTF8)
{
	if (m_pDocRoot == NULL)
		return NULL;
	wstring wstrBuffer;
	wstrBuffer += L"<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	m_pDocRoot->Serialize(NULL, wstrBuffer);
	return WideStringToUTF8(wstrBuffer, strUTF8);
}

int WMXMLParser::SerializeXML_UTF8(char **ppszUTF8)
{
	if (m_pDocRoot == NULL)
		return NULL;
	wstring wstrBuffer;
	wstrBuffer += L"<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	m_pDocRoot->Serialize(NULL, wstrBuffer);
	return WideStringToUTF8(wstrBuffer.c_str(), (unsigned long)wstrBuffer.length(), ppszUTF8);
}

bool WMXMLParser::SerializeXML_UTF16_LE(wstring& wstrBuffer)
{
	if (m_pDocRoot == NULL)
		return false;
	// insert BOM
	wstrBuffer = wchar_t(0xFFFE);
	wstrBuffer += L"<?xml version=\"1.0\" encoding=\"UTF-16\"?>";
	m_pDocRoot->Serialize(NULL, wstrBuffer);
	return true;
}

bool WMXMLParser::SerializeXML_UTF16_BE(wstring& wstrBuffer)
{
	if (SerializeXML_UTF16_LE(wstrBuffer) == false)
		return false;
	// reverse endian... BOM will now be 0xFEFF
	for (unsigned long i = 0; i < wstrBuffer.length(); i++)
	{
		wchar_t wc, wcHi, wcLo;
		wc = wstrBuffer[i];
		// make low byte
		wcLo = wc >> 8;
		// make high byte
		wcHi = wc << 8;
		// reverse endian
		wstrBuffer[i] = wcLo | wcHi;
	}
	return true;
}

// parse event helpers
/////////////////////////////////////////////////////////////////

void WMXMLParser::FireStartDocEvent()
{
	if (m_pEventHandler == NULL)
		return;
	m_pEventHandler->StartDocument();
}

void WMXMLParser::FireEndDocEvent()
{
	if (m_pEventHandler == NULL)
		return;
	m_pEventHandler->EndDocument();
}

void WMXMLParser::FireStartElementEvent(WMXMLElement *pElement)
{
	if (m_pEventHandler == NULL)
		return;
	m_pEventHandler->StartElement(pElement->GetURI(),
									pElement->GetLocalName(),
									pElement->GetQualifiedName(),
									pElement->GetAttributeMap(),
									pElement);
}

void WMXMLParser::FireEndElementEvent(WMXMLElement *pElement)
{
	if (m_pEventHandler == NULL)
		return;
	m_pEventHandler->EndElement(pElement->GetURI(),
									pElement->GetLocalName(),
									pElement->GetQualifiedName(),
									pElement);
}

void WMXMLParser::FireCharactersEvent(WMXMLElement *pElement)
{
	if (m_pEventHandler == NULL)
		return;
	m_pEventHandler->Characters(pElement->GetContent());
}

void WMXMLParser::FireFatalErrorEvent()
{
	if (m_pEventHandler == NULL)
		return;
	m_pEventHandler->FatalError(m_nLineNbr);
}

void WMXMLParser::FirePIEvent(const wstring& wstrTarget, const wstring& wstrContent)
{
	if (m_pEventHandler == NULL)
		return;
	m_pEventHandler->ProcessingInstruction(wstrTarget, wstrContent);
}

void WMXMLParser::FireStartNSMappingEvents(WMXMLElement *pElement, WMXMLElement *pParent)
{
	if (m_pEventHandler == NULL)
		return;
	if (pParent == NULL || pElement->GetDefaultNS() != pParent->GetDefaultNS())
		m_pEventHandler->StartNSPrefixMapping(wstring(L""), pElement->GetDefaultNS());

	for (NSPrefixMap::iterator iter = pElement->GetNSPrefixMap().begin();
						iter != pElement->GetNSPrefixMap().end(); iter++)
	{
		if (pParent == NULL || pParent->GetNSPrefixMap().find((*iter).first) == pParent->GetNSPrefixMap().end())
			m_pEventHandler->StartNSPrefixMapping((*iter).first, (*iter).second);
	}
}

void WMXMLParser::FireEndNSMappingEvents(WMXMLElement *pElement, WMXMLElement *pParent)
{
	if (m_pEventHandler == NULL)
		return;
	if (pParent == NULL || pElement->GetDefaultNS() != pParent->GetDefaultNS())
		m_pEventHandler->EndNSPrefixMapping(wstring(L""));

	for (NSPrefixMap::iterator iter = pElement->GetNSPrefixMap().begin();
						iter != pElement->GetNSPrefixMap().end(); iter++)
	{
		if (pParent == NULL || pParent->GetNSPrefixMap().find((*iter).first) == pParent->GetNSPrefixMap().end())
			m_pEventHandler->EndNSPrefixMapping((*iter).first);
	}
}

////////////////////////////////////////////////////////////////////////////
// Name (Type) class
////////////////////////////////////////////////////////////////////////////

WMXMLName::WMXMLName(const wstring& wstrQualifiedName) :
					m_wstrQualifiedName(wstrQualifiedName)
{
};

WMXMLName::~WMXMLName() {};

const wstring& WMXMLName::GetURI()
{
	return m_wstrURI;
}

const wstring& WMXMLName::GetLocalName()
{
	return m_wstrLocalName;
}

const wstring& WMXMLName::GetQualifiedName()
{
	return m_wstrQualifiedName;
}

void WMXMLName::SetURI(const wstring& wstrURI)
{
	m_wstrURI = wstrURI;
}

void WMXMLName::SetLocalName(const wstring& wstrLocalName)
{
	m_wstrLocalName = wstrLocalName;
}

void WMXMLName::SetQualifiedName(const wstring& wstrQualifiedName)
{
	m_wstrQualifiedName = wstrQualifiedName;
}

bool WMXMLName::Matches(WMXMLName& XMLName)
{
	return (m_wstrURI + m_wstrLocalName == XMLName.GetURI() + XMLName.GetLocalName());
}

////////////////////////////////////////////////////////////////////////////
// Attribute Classes
////////////////////////////////////////////////////////////////////////////

WMXMLAttribute::WMXMLAttribute(const wstring& wstrQualifiedName, const wstring& wstrValue) :
														WMXMLName(wstrQualifiedName)
{
	m_wstrValue = wstrValue;
};

WMXMLAttribute::WMXMLAttribute(WMXMLAttribute& Attr) : WMXMLName(Attr.GetQualifiedName())
{
	m_wstrElementName = Attr.GetElementName();
	m_wstrElementNS = Attr.GetElementNS();
	m_wstrValue = Attr.GetValue();
};

WMXMLAttribute::~WMXMLAttribute() {};

const wstring& WMXMLAttribute::GetValue()
{
	return m_wstrValue;
}

const wstring& WMXMLAttribute::GetElementName()
{
	return m_wstrElementName;
}

const wstring& WMXMLAttribute::GetElementNS()
{
	return m_wstrElementNS;
}

void WMXMLAttribute::SetElementName(const wstring& wstrName)
{
	m_wstrElementName = wstrName;
}

void WMXMLAttribute::SetElementNS(const wstring& wstrNS)
{
	m_wstrElementNS = wstrNS;
}

bool WMXMLAttribute::Matches(WMXMLAttribute& XMLAttr)
{
	return (GetURI() + GetElementNS() + GetElementName() + GetLocalName() ==
		XMLAttr.GetURI() + XMLAttr.GetElementNS() +  XMLAttr.GetElementName() + XMLAttr.GetLocalName());
}
 
WMXMLAttributeMap::WMXMLAttributeMap() {};

WMXMLAttributeMap::~WMXMLAttributeMap()
{
	for (WMXMLAttributeMap::iterator iAttr = begin(); iAttr != end(); iAttr++)
		delete (*iAttr).second;
};

void WMXMLAttributeMap::AddAttribute(const wstring& wstrQualifiedName,
									const wstring& wstrValue)
{
	WMXMLAttribute *pAttr = new WMXMLAttribute(wstrQualifiedName, wstrValue);
	if (pAttr != NULL)
		(*this)[wstrQualifiedName] = pAttr;
}

////////////////////////////////////////////////////////////////////////////
// WMXMLMarkup Class
////////////////////////////////////////////////////////////////////////////

WMXMLMarkup::WMXMLMarkup()
{
}

WMXMLMarkup::WMXMLMarkup(const wstring& wstrName)
{
}

WMXMLMarkup::~WMXMLMarkup()
{
}

const wstring& WMXMLMarkup::GetContent()
{
	return m_wstrContent;
}

void WMXMLMarkup::SetContent(const wstring& wstrValue)
{
	m_wstrContent = wstrValue;
}

void WMXMLMarkup::AppendContent(const wstring& wstrChars)
{
	m_wstrContent += wstrChars;
}

void WMXMLMarkup::AppendContent(wchar_t c)
{
	m_wstrContent += c;
}

////////////////////////////////////////////////////////////////////////////
// WMXMLElement Class
////////////////////////////////////////////////////////////////////////////

WMXMLElement::WMXMLElement(const wstring& wstrQualifiedName) : WMXMLName(wstrQualifiedName)
{
}

WMXMLElement::WMXMLElement(const wstring& wstrQualifiedName,
                           const wstring& wstrNamespaceURI,
                           const wstring& wstrLocalName) : WMXMLName(wstrQualifiedName)
{
    SetURI(wstrNamespaceURI);
    SetLocalName(wstrLocalName);
}

WMXMLElement::WMXMLElement(WMXMLElement& Elem) : WMXMLName(Elem.GetQualifiedName())
{
	// set local name
	SetLocalName(Elem.GetLocalName());
	// set namespace URI
	SetURI(Elem.GetURI());
	// copy prefix map
	m_mapNSPrefixes = Elem.m_mapNSPrefixes;
	// copy default ns
	SetDefaultNS(Elem.GetDefaultNS());
	// copy content
	AppendContent(Elem.GetContent());
	// copy list of children
	ChildElementsList::iterator iL;
	for (iL = Elem.GetChildElements().begin();
		 iL != Elem.GetChildElements().end();
		 iL++)
	{
		WMXMLElement *pDest = new WMXMLElement(*(*iL));
		if (pDest != NULL)
		{
			m_listChildren.push_back(pDest);
		}
	}
	// copy attribute list
	WMXMLAttributeMap::iterator iM;
	for (iM = Elem.GetAttributeMap().begin();
		 iM != Elem.GetAttributeMap().end();
		 iM++)
	{
		WMXMLAttribute *pDest = new WMXMLAttribute(*(iM->second));
		if (pDest != NULL)
		{
			m_mapAttributes.insert(WMXMLAttributeMap::value_type(iM->first, pDest));
		}
	}
}

WMXMLElement::~WMXMLElement()
{
	// empty child elements list
	ChildElementsList::iterator iElem = m_listChildren.begin();
	while (iElem != m_listChildren.end())
	{
        if (*iElem != NULL)
        {
		    delete *iElem;
        }
		iElem++;
	}
}

bool WMXMLElement::SetAttribute(const wstring& wstrName, const wstring& wstrValue)
{
	// check for dups
	if (m_mapAttributes.find(wstrName) != m_mapAttributes.end())
	{
		return false;
	}
	m_mapAttributes.AddAttribute(wstrName, wstrValue);
	return true;
}

bool WMXMLElement::SetNSPrefix(const wstring& wstrPrefix, const wstring& wstrNS)
{
	if (m_mapNSPrefixes.find(wstrPrefix) == m_mapNSPrefixes.end())
	{
		m_mapNSPrefixes[wstrPrefix] = wstrNS;
		return true;
	}
	return false;
}

bool WMXMLElement::GetAttribute(const wstring& wstrNS,
								const wstring& wstrLocalName,
								wstring& wstrValue)
{
	wstring wstrKey(wstrNS);
    if (wstrKey.empty() == true)
    {
        wstrKey = GetURI();
        wstrKey += GetLocalName();
    }
	wstrKey += wstrLocalName;
	// search for attribute
	WMXMLAttributeMap::iterator iM;
	if ((iM = m_mapAttributes.find(wstrKey)) == m_mapAttributes.end())
	{
		// not found
		return false;
	}
	// copy attribute value
	wstrValue = (iM->second)->GetValue();
	return true;
}

bool WMXMLElement::SetAttribute(const wstring& wstrQualifiedName,
								const wstring& wstrNS,
								const wstring& wstrValue)
{
	WMXMLAttribute *pAttr = new WMXMLAttribute(wstrQualifiedName, wstrValue);
	if (pAttr == NULL)
	{
		return false;
	}
	wstring wstrKey;
	wstring::size_type pos;
	if ((pos = wstrQualifiedName.find(wchar_t(':'))) != wstring::npos)
	{
		wstring wstrPrefix = wstrQualifiedName.substr(0, pos);
		if (wstrPrefix.empty() != true)
		{
			// look for prefix in map
			NSPrefixMap::iterator iM;
			if ((iM = m_mapNSPrefixes.find(wstrPrefix)) == m_mapNSPrefixes.end())
			{
                // insert prefix mapping
				m_mapNSPrefixes[wstrPrefix] = wstrNS;
			}
			else
			{
				// does namespace match?
				if (iM->second != wstrNS)
				{
					delete pAttr;
					return false;
				}
			}
			// local name must not contain any colons
			if (pAttr->GetLocalName().find(wchar_t(':')) != string::npos)
			{
				delete pAttr;
				return false;
			}
			pAttr->SetURI(wstrNS);
			pAttr->SetLocalName(wstrQualifiedName.substr(++pos));
			// set key
			wstrKey = pAttr->GetURI() + pAttr->GetLocalName();
		}
		else
		{
			// name can't begin with ':'
			delete pAttr;
			return false;
		}
	}
	else
	{
		// unqualified attribute
		pAttr->SetLocalName(pAttr->GetQualifiedName());
		pAttr->SetElementName(GetLocalName());
		pAttr->SetElementNS(GetURI());
		// set key
		wstrKey = pAttr->GetElementNS() + pAttr->GetElementName() + pAttr->GetLocalName();
	}
	// check for dups
	if (m_mapAttributes.find(wstrKey) != m_mapAttributes.end())
	{
		// already there
		delete pAttr;
		return true;
	}
	m_mapAttributes[wstrKey] = pAttr;
	return true;
}

bool WMXMLElement::ProcNamespaceDecl(WMXMLAttribute *pAttribute)
{
	wstring wstrQualName = pAttribute->GetQualifiedName();
	if (wstrQualName == L"xmlns")
	{
		m_wstrDefNameSpace = pAttribute->GetValue();
		// insert default NS in map with empty key
		m_mapNSPrefixes[wstring(L"")] = pAttribute->GetValue();
		return true;
	}
	wstring::size_type pos;
	if ((pos = wstrQualName.find(wchar_t(':'))) != wstring::npos)
	{
		wstring wstrXmlns = wstrQualName.substr(0, pos);
		if (wstrXmlns == L"xmlns")
		{
			m_mapNSPrefixes[wstrQualName.substr(++pos)] = pAttribute->GetValue();
		}
		else
		{
			return false;
		}
		return true;
	}
	return false;
}

bool WMXMLElement::ResolveNSPrefixes(WMXMLElement *pParent)
{
	// if there is a parent then inherit ns map
	if (pParent != NULL)
	{
		m_mapNSPrefixes = pParent->GetNSPrefixMap();
		m_wstrDefNameSpace = pParent->m_wstrDefNameSpace;
	}
    else
    {
        // root, so insert reserved "xml" prefix
		m_mapNSPrefixes[wstring(L"xml")] = wstring(L"http://www.w3.org/XML/1998/namespace");
    }
	// now process any ns prefix declarations contained in attributes
	WMXMLAttributeMap::iterator iter = m_mapAttributes.begin();
	while (iter != m_mapAttributes.end())
	{
		WMXMLAttribute *pAttr = (*iter).second;
		if (ProcNamespaceDecl(pAttr) == true)
		{
			// this was a ns prefix declaration, don't need it anymore
			delete pAttr;
			iter = m_mapAttributes.erase(iter);
		}
		else
		{
			iter++;
		}
	}	
	// resolve own name
	wstring wstrQualName = GetQualifiedName();
	wstring::size_type pos;
	if ((pos = wstrQualName.find(wchar_t(':'))) != wstring::npos)
	{
		wstring wstrPrefix = wstrQualName.substr(0, pos);
		if (wstrPrefix.empty() != true)
		{
			// global
			if (m_mapNSPrefixes.find(wstrPrefix) == m_mapNSPrefixes.end())
				return false;
			SetURI(m_mapNSPrefixes[wstrPrefix]);
			SetLocalName(wstrQualName.substr(++pos));
			// must not contain any colons
			if (GetLocalName().find(wchar_t(':')) != wstring::npos)
				return false;
		}
		else
		{
			SetURI(m_wstrDefNameSpace);
			SetLocalName(GetQualifiedName());
		}
	}
	else
	{
		SetURI(m_wstrDefNameSpace);
		SetLocalName(GetQualifiedName());
	}
	// resolve attribute names
	map<wstring, WMXMLAttribute *> mapTemp;
	wstring wstrKey;
	for (iter = m_mapAttributes.begin(); iter != m_mapAttributes.end(); iter++)
	{
		WMXMLAttribute *pAttr = (*iter).second;
		wstring wstrQualName = pAttr->GetQualifiedName();
		wstring::size_type pos;
		if ((pos = wstrQualName.find(wchar_t(':'))) != wstring::npos)
		{
			wstring wstrPrefix = wstrQualName.substr(0, pos);
			if (wstrPrefix.empty() != true)
			{
				// global
				if (m_mapNSPrefixes.find(wstrPrefix) == m_mapNSPrefixes.end())
				{
					return false;
				}
				// local name must not contain any colons
				if (pAttr->GetLocalName().find(wchar_t(':')) != string::npos)
				{
					return false;
				}
				pAttr->SetURI(m_mapNSPrefixes[wstrPrefix]);
				pAttr->SetLocalName(wstrQualName.substr(++pos));
				// set key
				wstrKey = pAttr->GetURI() + pAttr->GetLocalName();
			}
			else
			{
				// name can't begin with ':'
				return false;
			}
		}
		else
		{
			// unqualified attribute
			pAttr->SetLocalName(pAttr->GetQualifiedName());
			pAttr->SetElementName(GetLocalName());
			pAttr->SetElementNS(GetURI());
			// set key
			wstrKey = pAttr->GetElementNS() + pAttr->GetElementName() + pAttr->GetLocalName();
		}
		// check for dups
		if (mapTemp.find(wstrKey) != mapTemp.end())
			return false;
		mapTemp[wstrKey] = pAttr;
	}
	m_mapAttributes.clear();
	map<wstring, WMXMLAttribute *>::iterator iter2 = mapTemp.begin();
	while (iter2 != mapTemp.end())
	{
		m_mapAttributes.insert(WMXMLAttributeMap::value_type((*iter2).first, (*iter2).second));
		iter2++;
	}
	return true;
}

WMXMLAttributeMap& WMXMLElement::GetAttributeMap()
{
	return m_mapAttributes;
}

void WMXMLElement::AddChildElement(WMXMLElement *pChild)
{
	if (pChild != NULL)
		m_listChildren.push_back(pChild);
}

WMXMLElement *WMXMLElement::GetChildElement(const wstring& wstrNS,
											const wstring& wstrLocalName)
{
	ChildElementsList::iterator iL;
	for (iL = m_listChildren.begin(); iL != m_listChildren.end(); iL++)
	{
		WMXMLElement *pChild = *iL;
		if (pChild->GetLocalName() == wstrLocalName &&
			pChild->GetURI() == wstrNS)
		{
			return pChild;
		}
	}
	return NULL;
}

WMXMLElement *WMXMLElement::GetNestedElement(	const wstring& wstrNS,
												const wstring& wstrLocalName)
{
	ChildElementsList::iterator iL;
	for (iL = m_listChildren.begin(); iL != m_listChildren.end(); iL++)
	{
		WMXMLElement *pChild = *iL;
		if (pChild->GetLocalName() == wstrLocalName &&
			pChild->GetURI() == wstrNS)
		{
			// this is it
			return pChild;
		}
		else
		{
			WMXMLElement *pNested = pChild->GetNestedElement(wstrNS, wstrLocalName);
			if (pNested != NULL)
			{
				// this is it
				return pNested;
			}
		}
	}
	return NULL;
}

ChildElementsList& WMXMLElement::GetChildElements()
{
	return m_listChildren;
}

NSPrefixMap& WMXMLElement::GetNSPrefixMap()
{
	return m_mapNSPrefixes;
}

wstring& WMXMLElement::GetDefaultNS()
{
	return m_wstrDefNameSpace;
}

void WMXMLElement::SetDefaultNS(const wstring& wstrNS)
{
    m_wstrDefNameSpace = wstrNS;
}

BSTR WMXMLElement::Serialize(WMXMLElement *pParent)
{
	wstring wstrTemp;
	Serialize(pParent, wstrTemp);
	return SysAllocString(wstrTemp.c_str());
}

BSTR WMXMLElement::Serialize(WMXMLElement *pParent, int nLevel, int nIndent)
{
	wstring wstrTemp;
	Serialize(pParent, wstrTemp, nLevel, nIndent);
	return SysAllocString(wstrTemp.c_str());
}

void WMXMLElement::Serialize(WMXMLElement *pParent, wstring& wstrBuffer)
{
	wstring wstrTemp;
	// opening tag
	wstrBuffer += L"<";
	wstrBuffer += GetQualifiedName();
	// attributes
	WMXMLAttributeMap::iterator iA = m_mapAttributes.begin();
	for (; iA != m_mapAttributes.end(); iA++)
	{
		wstrBuffer += wchar_t(' ');
		wstrBuffer += (*iA).second->GetQualifiedName();
		wstrBuffer += L"=\"";
		// escape illegal chars in attr value
		wstrTemp = (*iA).second->GetValue();
		EscAttributeValue(wstrTemp);
		wstrBuffer += wstrTemp;
		wstrBuffer += wchar_t('\"');
	}
	// namespace attributes
	NSPrefixMap::iterator iNS = m_mapNSPrefixes.begin();
	for (; iNS != m_mapNSPrefixes.end(); iNS++)
	{
        // ignore built in xml namespace
        if ((*iNS).first == L"xml")
        {
            continue;
        }
		if (pParent != NULL)
		{
			// if in the parent map then skip it
			NSPrefixMap::iterator iPNS;
			iPNS = pParent->GetNSPrefixMap().find((*iNS).first);
			if (iPNS != pParent->GetNSPrefixMap().end())
			{
				if ((*iNS).second == (*iPNS).second)
					continue;
			}
		}
		if ((*iNS).first.empty() == true)
		{
			// default ns
			wstrBuffer += L" xmlns";
		}
		else
		{
			wstrBuffer += L" xmlns:";
			wstrBuffer += (*iNS).first;
		}
		wstrBuffer += L"=\"";
		// escape illegal chars in attr value
		wstrTemp = (*iNS).second;
		EscAttributeValue(wstrTemp);
		wstrBuffer += wstrTemp;
		wstrBuffer += wchar_t('\"');
	}
	// empty?
	if (GetContent().empty() == true && m_listChildren.empty() == true)
	{
		// done
		wstrBuffer += L" />";
		return;
	}
	wstrBuffer += wchar_t('>');
	// content (illegal chars escaped)
	wstrTemp = GetContent();
	EscElementContent(wstrTemp);
	wstrBuffer += wstrTemp;
	// child elements
	ChildElementsList::iterator iC = m_listChildren.begin();
	for (; iC != m_listChildren.end(); iC++)
	{
		(*iC)->Serialize(this, wstrBuffer);
	}
	// ending tag
	wstrBuffer += L"</";
	wstrBuffer += GetQualifiedName();
	wstrBuffer += L">";
}

void WMXMLElement::Serialize(   WMXMLElement *pParent,
                                wstring& wstrBuffer,
                                int nLevel,
                                int nIndent)
{
	wstring wstrTemp;
	// opening tag
	wstrBuffer += L"\r\n";
    wstrBuffer.append(nLevel * nIndent, wchar_t(' '));
	wstrBuffer += L"<";
	wstrBuffer += GetQualifiedName();
	// attributes
	WMXMLAttributeMap::iterator iA = m_mapAttributes.begin();
	for (; iA != m_mapAttributes.end(); iA++)
	{
		wstrBuffer += wchar_t(' ');
		wstrBuffer += (*iA).second->GetQualifiedName();
		wstrBuffer += L"=\"";
		// escape illegal chars in attr value
		wstrTemp = (*iA).second->GetValue();
		EscAttributeValue(wstrTemp);
		wstrBuffer += wstrTemp;
		wstrBuffer += wchar_t('\"');
	}
	// namespace attributes
	NSPrefixMap::iterator iNS = m_mapNSPrefixes.begin();
	for (; iNS != m_mapNSPrefixes.end(); iNS++)
	{
        // ignore built in xml namespace
        if ((*iNS).first == L"xml")
        {
            continue;
        }
		if (pParent != NULL)
		{
			// if in the parent map then skip it
			NSPrefixMap::iterator iPNS;
			iPNS = pParent->GetNSPrefixMap().find((*iNS).first);
			if (iPNS != pParent->GetNSPrefixMap().end())
			{
				if ((*iNS).second == (*iPNS).second)
					continue;
			}
		}
		if ((*iNS).first.empty() == true)
		{
			// default ns
			wstrBuffer += L" xmlns";
		}
		else
		{
			wstrBuffer += L" xmlns:";
			wstrBuffer += (*iNS).first;
		}
		wstrBuffer += L"=\"";
		// escape illegal chars in attr value
		wstrTemp = (*iNS).second;
		EscAttributeValue(wstrTemp);
		wstrBuffer += wstrTemp;
		wstrBuffer += wchar_t('\"');
	}
	// empty?
	if (GetContent().empty() == true && m_listChildren.empty() == true)
	{
		// done
		wstrBuffer += L" />";
		return;
	}
	if (GetContent().empty() == false && m_listChildren.empty() == false)
    {
        SetContent(wstring(L""));
    }
	wstrBuffer += wchar_t('>');
	// content (illegal chars escaped)
	wstrTemp = GetContent();
	EscElementContent(wstrTemp);
	wstrBuffer += wstrTemp;
	// child elements
	ChildElementsList::iterator iC = m_listChildren.begin();
	for (; iC != m_listChildren.end(); iC++)
	{
		(*iC)->Serialize(this, wstrBuffer, nLevel + 1);
	}
	// ending tag
    if (m_listChildren.empty() == false)
    {
        wstrBuffer += L"\r\n";
        wstrBuffer.append(nLevel * nIndent, wchar_t(' '));
    }
	wstrBuffer += L"</";
	wstrBuffer += GetQualifiedName();
	wstrBuffer += L">";
}


void WMXMLElement::EscElementContent(wstring& wstrContent)
{
	for (wstring::size_type i = 0; i < wstrContent.length(); i++)
	{
		wchar_t wc = wstrContent[i];
		if (wc == wchar_t('&'))
		{
			wstrContent.insert(++i, L"amp;");
		}
		else if (wc == wchar_t('<'))
		{
			wstrContent[i] = wchar_t('&');
			wstrContent.insert(++i, L"lt;");
		}
		else if (wc == wchar_t('>'))
		{
			wstrContent[i] = wchar_t('&');
			wstrContent.insert(++i, L"gt;");
		}
	}
}

void WMXMLElement::EscAttributeValue(wstring& wstrAttValue)
{
	for (wstring::size_type i = 0; i < wstrAttValue.length(); i++)
	{
		wchar_t wc = wstrAttValue[i];
		if (wc == wchar_t('&'))
		{
			wstrAttValue.insert(++i, L"amp;");
		}
		else if (wc == wchar_t('<'))
		{
			wstrAttValue[i] = wchar_t('&');
			wstrAttValue.insert(++i, L"lt;");
		}
		else if (wc == wchar_t('\''))
		{
			wstrAttValue[i] = wchar_t('&');
			wstrAttValue.insert(++i, L"apos;");
		}
		else if (wc == wchar_t('\"'))
		{
			wstrAttValue[i] = wchar_t('&');
			wstrAttValue.insert(++i, L"quot;");
		}
	}
}
