// SOAPElements.h: interface for the WMSOAPElements class.
//
//////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000 - 2002 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.
//
//////////////////////////////////////////////////////////////////////
//
// SOAPElements.h - a collection of event handlers used to process SOAP XML.
// These handlers are pushed onto the processing stack as required by the
// main parser event handler owned by the SOAP processor.
// They are derived from the abstract base class 'WMSOAPEventHandler'.
// Also here is the SOAP fault class. These classes recoginize a subset of
// the SOAP 1.1 and 1.2 encoding rules. Simple data types 
// (including SOAP-ENC:base64), arrays (including multidimensional) and 
// Structs are supported. Multi-reference values are supported. Enumerations
// are not supported. SOAP 1.1 Array support is partial: Partially transmitted arrays are
// supported, but sparse arrays are not. 'xsi:type' attributes are captured
// if found. SOAP 1.2 'nodeType' attributes are supported and validated against
// the type definitions in the WSDL document for RPC style operations.
//
// The 'actor' (SOAP 1.1), 'role' (SOAP 1.2), 'relay' (SOAP 1.2) and 'mustUnderstand'
// attributes are recognized in header blocks.
//
// SOAP versioning per the 1.1 and 1.2 specs is enforced.
//
//////////////////////////////////////////////////////////////////////


#if !defined(AFX_SOAPELEMENTS_H__443CE236_34D5_11D4_9E67_00105AABC815__INCLUDED_)
#define AFX_SOAPELEMENTS_H__443CE236_34D5_11D4_9E67_00105AABC815__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "XMLParser.h"
#include "base64.h"
#include "charencode.h"
#include "soap.h"
 
#define IGNORE_ELEMENT_HANDLER NULL

class WMSOAPFault
{
public:

    typedef enum enumSoapFaultCode
    {
        VersionMismatch,
        MustUnderstand,
        DataEncodingUnknown,
        Sender,
        Receiver
    } SOAP_FAULT_CODE;

	WMSOAPFault(WMSOAPFault::SOAP_FAULT_CODE SoapFaultCode,
				const wchar_t *pwszFaultString,
				const wchar_t *pwszDetailName,
				const wchar_t *pwszDetailNS,
				const _variant_t& varDetailContent)
	{
		m_SoapFaultCode = SoapFaultCode;
		m_wstrFaultString = pwszFaultString;
		m_bNeedDetail = true;
		m_wstrDetailName = pwszDetailName;
		m_wstrDetailNS = pwszDetailNS;
		m_varDetailContent = varDetailContent;
	}
	WMSOAPFault(WMSOAPFault::SOAP_FAULT_CODE SoapFaultCode,
                const wchar_t *pwszSubCode,
                const wchar_t *pwszSubCodeNS,
				const wchar_t *pwszFaultString,
				const wchar_t *pwszDetailName,
				const wchar_t *pwszDetailNS,
				const _variant_t& varDetailContent)
	{
		m_SoapFaultCode = SoapFaultCode;
        m_wstrSubCode = pwszSubCode,
        m_wstrSubCodeNS = pwszSubCodeNS,
		m_wstrFaultString = pwszFaultString;
		m_bNeedDetail = true;
		m_wstrDetailName = pwszDetailName;
		m_wstrDetailNS = pwszDetailNS;
		m_varDetailContent = varDetailContent;
	}
    WMSOAPFault(WMSOAPFault::SOAP_FAULT_CODE SoapFaultCode,
				const wchar_t *pwszFaultString,
				bool bNeedDetail)
	{
        m_SoapFaultCode = SoapFaultCode;
		m_wstrFaultString = pwszFaultString;
		m_bNeedDetail = bNeedDetail;
	}
    WMSOAPFault(WMSOAPFault::SOAP_FAULT_CODE SoapFaultCode,
                const wchar_t *pwszSubCode,
                const wchar_t *pwszSubCodeNS,
				const wchar_t *pwszFaultString,
				bool bNeedDetail)
	{
        m_SoapFaultCode = SoapFaultCode;
        m_wstrSubCode = pwszSubCode,
        m_wstrSubCodeNS = pwszSubCodeNS,
		m_wstrFaultString = pwszFaultString;
		m_bNeedDetail = bNeedDetail;
	}
	WMSOAPFault() { m_bNeedDetail = false; }
	~WMSOAPFault() {}
    void GetSoapFaultCode(int nSOAPVersion, wstring& wstrCode, wstring& wstrNS)
    {
        switch(m_SoapFaultCode)
        {
        case VersionMismatch:
            wstrCode = L"VersionMismatch";
            break;
        case MustUnderstand:
            wstrCode = L"MustUnderstand";
            break;
        case DataEncodingUnknown:
            wstrCode = L"DataEncodingUnknown";
            break;
        case Sender:
            if (nSOAPVersion == SOAP_VERSION_1_1)
                wstrCode = L"Client";
            else
                wstrCode = L"Sender";
            break;
        case Receiver:
            if (nSOAPVersion == SOAP_VERSION_1_1)
                wstrCode = L"Server";
            else
                wstrCode = L"Receiver";
            break;
        }
        if (nSOAPVersion == SOAP_VERSION_1_1)
        {
            wstrNS = g_pwszSoapEnvelopeNS;
        }
        else
        {
            // SOAP 1.2
            wstrNS = g_pwszSoap12EnvelopeNS;
        }
    }
    void GetFaultSubCode(wstring& wstrCode, wstring& wstrNS)
    {
        wstrCode = m_wstrSubCode;
        wstrNS = m_wstrSubCodeNS;
    }
    void SetFaultSubCode(const wstring& wstrCode, const wstring& wstrNS)
    {
        m_wstrSubCode = wstrCode;
        m_wstrSubCodeNS = wstrNS;
    }
	const wstring& GetFaultString() { return m_wstrFaultString; }
	const wstring& GetReason() { return m_wstrFaultString; }
	bool NeedDetail() { return m_bNeedDetail; }
    void SetNeedDetail(bool bValue) { m_bNeedDetail = bValue; }
	const wstring& GetDetailName() { return m_wstrDetailName; }
	const wstring& GetDetailNS() { return m_wstrDetailNS; }
	const _variant_t GetDetailContent() { return m_varDetailContent; }

private:
	SOAP_FAULT_CODE	    m_SoapFaultCode;
    wstring             m_wstrSubCode;
    wstring             m_wstrSubCodeNS;
	wstring		        m_wstrFaultString;
	bool		        m_bNeedDetail;
	wstring		        m_wstrDetailName;
	wstring		        m_wstrDetailNS;
	_variant_t	        m_varDetailContent;
};

// abstract base class for SOAP event handlers
class WMSOAPEventHandler
{
public:

	WMSOAPEventHandler() { m_nSOAPVersion = SOAP_VERSION_1_1; };
	virtual ~WMSOAPEventHandler() {};

    virtual WMSOAPEventHandler *StartElement(const wstring& wstrURI,
											const wstring& wstrLocalName,
											const wstring& wstrQualifiedName,
											WMXMLAttributeMap& mapAttributes,
											NSPrefixMap& mapNSPrefixes,
											WMXMLElement *pElement) = 0;

	virtual bool EndElement(const wstring& wstrURI,
					const wstring& wstrLocalName,
					const wstring& wstrQualifiedName,
					WMXMLElement *pElement) { return true; }

    virtual void Characters(const wstring& wstrChars) {}

    void SOAPFault( WMSOAPFault::SOAP_FAULT_CODE SoapFaultCode,
					const wchar_t *pwszFaultString,
					bool bNeedDetail)
	{
	    WMSOAPFault *pFault = new WMSOAPFault(  SoapFaultCode,
												pwszFaultString,
												bNeedDetail);
	    throw pFault;
	}
    void SOAPFault( WMSOAPFault::SOAP_FAULT_CODE SoapFaultCode,
                    const wchar_t *pwszSubCode,
                    const wchar_t *pwszSubCodeNS,
				    const wchar_t *pwszFaultString,
					bool bNeedDetail)
	{
	    WMSOAPFault *pFault = new WMSOAPFault(  SoapFaultCode,
                                                pwszSubCode,
                                                pwszSubCodeNS,
												pwszFaultString,
												bNeedDetail);
	    throw pFault;
	}
    void SetSOAPVersion(int nVersion) { m_nSOAPVersion = nVersion; }
    int GetSOAPVersion() { return m_nSOAPVersion; }

protected:
    int m_nSOAPVersion;
};

class WMSOAPNullHandler : public WMSOAPEventHandler
{
public:
    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement) { return IGNORE_ELEMENT_HANDLER; }

	bool EndElement(const wstring& wstrURI,
					const wstring& wstrLocalName,
					const wstring& wstrQualifiedName,
					WMXMLElement *pElement) { delete this; return true; }
};

class WMSOAPCharacterHandler : public WMSOAPEventHandler
{
public:
	WMSOAPCharacterHandler(wstring& wstrContent) : m_wstrContent(wstrContent) {}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement) { return IGNORE_ELEMENT_HANDLER; }

	bool EndElement(const wstring& wstrURI,
					const wstring& wstrLocalName,
					const wstring& wstrQualifiedName,
					WMXMLElement *pElement) { delete this; return true; }

	void Characters(const wstring& wstrChars) { m_wstrContent = wstrChars; }

private:
	wstring&	m_wstrContent;
};

class WMSOAPElemHandler : public WMSOAPEventHandler
{
public:
	WMSOAPElemHandler() { m_pXMLElement = NULL; }
	WMSOAPElemHandler(const wstring& wstrURI, const wstring& wstrLocalName) :
				m_wstrURI(wstrURI), m_wstrLocalName(wstrLocalName) {}
	virtual ~WMSOAPElemHandler() {}

    virtual void ReadAttributes(WMXMLAttributeMap& mapAttributes, NSPrefixMap& mapNSPrefixes)
	{
		wstring wstrKey;
        if (m_nSOAPVersion == SOAP_VERSION_1_1)
        {
            wstrKey = g_pwszSoapEnvelopeNS;
        }
        else
        {
            wstrKey = g_pwszSoap12EnvelopeNS;
        }
		wstrKey += L"encodingStyle";
		WMXMLAttributeMap::iterator iter;
		if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		{
			m_wstrEncodingStyle = (*iter).second->GetValue();
		}
	}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement)
	{
		return IGNORE_ELEMENT_HANDLER;
	}

	bool EndElement(const wstring& wstrURI,
					const wstring& wstrLocalName,
					const wstring& wstrQualifiedName,
					WMXMLElement *pElement)
	{
		SetXMLElement(pElement);
		return true;
	}

	const wstring& GetURI() { return m_wstrURI;}
	const wstring& GetLocalName() { return m_wstrLocalName;}
	const wstring& GetEncodingStyle() { return m_wstrEncodingStyle;}
	virtual void SetEncodingStyle(const wstring& wstrStyle) { m_wstrEncodingStyle = wstrStyle; }

	void SetXMLElement(WMXMLElement *pElement)
	{
		m_pXMLElement = pElement;
	}
	WMXMLElement *GetXMLElement() { return m_pXMLElement; }

private:
	wstring		m_wstrLocalName;
	wstring		m_wstrURI;
	wstring		m_wstrEncodingStyle;
	// XML element object
	WMXMLElement *m_pXMLElement;
};
 
class WMSOAPParamHandler : public WMSOAPElemHandler
{
public:
	WMSOAPParamHandler(const wstring& wstrURI,
						const wstring& wstrLocalName,
						map<wstring, WMSOAPParamHandler	*>	*pmapMRE) :
									  WMSOAPElemHandler(wstrURI, wstrLocalName)
	{
		m_pmapMultiRefElems = pmapMRE;
        // array defaults
		m_lNumElems = -1;
	    m_arrArrayDims.push_back(0);
	}
	virtual ~WMSOAPParamHandler()
	{
		vector<WMSOAPParamHandler *>::iterator iV;
		for (iV = m_arrChildren.begin();
			iV != m_arrChildren.end(); iV++)
		{
			delete *iV;
		}
	}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement)
	{
		WMSOAPParamHandler *pParam = new WMSOAPParamHandler(wstrURI,
															wstrLocalName,
															m_pmapMultiRefElems);
		m_arrChildren.push_back(pParam);
        pParam->SetSOAPVersion(m_nSOAPVersion);
        pParam->SetEncodingStyle(GetEncodingStyle());
		pParam->ReadAttributes(mapAttributes, mapNSPrefixes);
		// does it have id attribute?
		if (pParam->GetId().empty() == false)
		{
			// yes, so place in multi-ref map
            if (m_pmapMultiRefElems->find(pParam->GetId()) == m_pmapMultiRefElems->end())
            {
			    m_pmapMultiRefElems->insert(map<wstring, WMSOAPParamHandler *>::value_type(pParam->GetId(), pParam));
            }
            else
            {
                if (m_nSOAPVersion == SOAP_VERSION_1_2)
                {
			        SOAPFault(WMSOAPFault::Sender,
                                L"DuplicateID",
                                g_pwszSoap12EncodingNS,
						        L"Id value found on accessor element is a duplicate.",
                                true);
		        }
			    SOAPFault(WMSOAPFault::Sender,
						    L"Id value found on accessor element is a duplicate.",
                            true);
            }
        }
		return pParam;
	}

	bool EndElement(const wstring& wstrURI,
					const wstring& wstrLocalName,
					const wstring& wstrQualifiedName,
					WMXMLElement *pElement)
	{
		SetXMLElement(pElement);
		return true;
	}

	void Characters(const wstring& wstrChars)
	{
		m_wstrCharContent = wstrChars;
	}

    void ReadAttributes(WMXMLAttributeMap& mapAttributes, NSPrefixMap& mapNSPrefixes)
	{
		WMSOAPElemHandler::ReadAttributes(mapAttributes, mapNSPrefixes);
		wstring wstrBase(GetURI());
		wstrBase += GetLocalName();
		wstring wstrKey = wstrBase;
		WMXMLAttributeMap::iterator iter;
        // SOAP 1.1 encoding attributes
        if (m_nSOAPVersion == SOAP_VERSION_1_1)
        {
		    // process any id attribute
		    wstrKey += L"id";
		    if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		    {
			    m_wstrId = (*iter).second->GetValue();
		    }
		    // process any href attribute
		    wstrKey = wstrBase;
		    wstrKey += L"href";
		    if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		    {
			    m_wstrHref = (*iter).second->GetValue();
		    }
		    // process any SOAP-ENC:arrayType attribute
		    wstrKey = g_pwszSoapEncodingNS;
		    wstrKey += L"arrayType";
		    if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		    {
			    // found SOAP-ENC:arrayType attribute
			    WMXMLAttribute *pAttr = (*iter).second;
			    SetArrayDataType(pAttr->GetValue(), mapNSPrefixes);
		    }
		    // process any SOAP-ENC:offset attribute
		    wstrKey = g_pwszSoapEncodingNS;
		    wstrKey += L"offset";
		    if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		    {
			    // found SOAP-ENC:offset attribute
			    WMXMLAttribute *pAttr = (*iter).second;
			    SetArrayOffsets(pAttr->GetValue());
		    }
		    // process any SOAP-ENC:root attribute
		    wstrKey = g_pwszSoapEncodingNS;
		    wstrKey += L"root";
		    if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		    {
			    m_wstrRoot = (*iter).second->GetValue();
		    }
        }
        // SOAP 1.2 encoding attributes
        else if (m_nSOAPVersion == SOAP_VERSION_1_2)
        {
		    // process any SOAP-ENC:id attribute
            bool bIdFound = false;
            bool bRefFound = false;
            wstrKey = g_pwszSoap12EncodingNS;
		    wstrKey += L"id";
		    if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		    {
			    m_wstrId = (*iter).second->GetValue();
                bIdFound = true;
		    }
		    // process any SOAP-ENC:ref attribute
            wstrKey = g_pwszSoap12EncodingNS;
		    wstrKey += L"ref";
		    if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		    {
			    m_wstrHref = (*iter).second->GetValue();
                bRefFound = true;
		    }
            if (bIdFound == true && bRefFound == true)
			{
				SOAPFault(WMSOAPFault::Sender,
						L"Encountered  attributes 'type' and 'id' on same element.", true);
			}
		    // process any SOAP-ENC:nodeType attribute
		    wstrKey = g_pwszSoap12EncodingNS;
		    wstrKey += L"nodeType";
		    if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		    {
			    m_wstrNodeType = (*iter).second->GetValue();
                if (m_wstrNodeType != L"simple" &&
                    m_wstrNodeType != L"struct" &&
                    m_wstrNodeType != L"array")
			    {
                    // invalid
				    SOAPFault(WMSOAPFault::Sender,
						    L"Encountered invalid value for attribute 'nodeType' on accessor element.", true);
			    }
		    }
		    // process any SOAP-ENC:itemType attribute
		    wstrKey = g_pwszSoap12EncodingNS;
		    wstrKey += L"itemType";
		    if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		    {
			    // found SOAP-ENC:itemType attribute
			    WMXMLAttribute *pAttr = (*iter).second;
			    SetItemType(pAttr->GetValue(), mapNSPrefixes);
		    }
		    // process any SOAP-ENC:arraySize attribute
		    wstrKey = g_pwszSoap12EncodingNS;
		    wstrKey += L"arraySize";
		    if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		    {
			    // found SOAP-ENC:offset attribute
			    WMXMLAttribute *pAttr = (*iter).second;
			    SetArraySize(pAttr->GetValue());
		    }
        }
		// process any xsi:nil attribute
		wstrKey = g_pwszXMLSchemaInstNS2001;
		wstrKey += L"nil";
		if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		{
			m_wstrNil = (*iter).second->GetValue();
		}
		else
		{
			wstrKey = g_pwszXMLSchemaInstNS1999;
			wstrKey += L"null";
			if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
			{
				m_wstrNil = (*iter).second->GetValue();
			}
			else
			{
				wstrKey = g_pwszXMLSchemaInstNS2000;
				wstrKey += L"nil";
				if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
				{
					m_wstrNil = (*iter).second->GetValue();
				}
			}
		}
		// process any xsi:type attribute
		wstrKey = g_pwszXMLSchemaInstNS2001;
		wstrKey += L"type";
		if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		{
			goto processtype;
		}
		else
		{
			wstrKey = g_pwszXMLSchemaInstNS1999;
			wstrKey += L"type";
			if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
			{
				goto processtype;
			}
			else
			{
				wstrKey = g_pwszXMLSchemaInstNS2000;
				wstrKey += L"type";
				if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
				{
					goto processtype;
				}
			}
		}
		return;

processtype:
		WMXMLAttribute *pAttr = (*iter).second;
		// check for namespace qualified data type value
		wstring wstrTemp = pAttr->GetValue();
		wstring::size_type pos = wstrTemp.find(':');
		if (pos != wstring::npos)
		{
			// get NS prefix
			NSPrefixMap::iterator i;
			if ((i = mapNSPrefixes.find(wstrTemp.substr(0, pos))) != mapNSPrefixes.end())
			{
				m_wstrTypeNS = (*i).second;
				m_wstrType = wstrTemp.substr(++pos);
			}
			else
			{
				// namespace not found in NS map
                SOAPFault(WMSOAPFault::Sender,
						L"Encountered undeclared namespace prefix for attribute 'type' on element.", true);
			}
		}
		else
		{
			// default namespace
			NSPrefixMap::iterator iNS;
			if ((iNS = mapNSPrefixes.find(wstring(L""))) != mapNSPrefixes.end())
			{
				m_wstrTypeNS = (*iNS).second;
				m_wstrType = wstrTemp;
			}
			else
			{
				SOAPFault(WMSOAPFault::Sender,
						L"Encountered unresolvable namespace for attribute 'type' on element.", true);
			}
		}
	}

	vector<WMSOAPParamHandler *>& GetChildren() { return m_arrChildren; }
	const vector<long>& GetArrayDims() { return m_arrArrayDims; };
	const vector<long>& GetArrayOffsets() { return m_arrArrayOffsets; };
	long GetNumElems() { return m_lNumElems; };
	// type from SOAP-ENC:arrayType attribute
	const wstring& GetArrayElemType() { return m_wstrArrayElemType; }
	const wstring& GetArrayElemTypeNS() { return m_wstrArrayElemTypeNS; }
	// value from SOAP-ENC:offset attribute
	const wstring& GetOffset() { return m_wstrOffset; }
	// value from SOAP-ENC:root attribute
	const wstring& GetRoot() { return m_wstrRoot; }
	// character content
	const wstring& GetCharContent() { return m_wstrCharContent; }
	// type from xsi:type attribute
	const wstring& GetType() { return m_wstrType; }
	// namespace of type from xsi:type attribute
	const wstring& GetTypeNS() { return m_wstrTypeNS; }
	// is xsi:type give?
	bool IsExplicitlyTyped() { return !m_wstrType.empty(); }
	// value of xsi:nil (xsi:null in 1999 schema) attribute
	bool IsNil() { return (m_wstrNil == L"true" || m_wstrNil == L"1"); }
    // SOAP 1.2 enc:nodeType
    bool NodeTypeAsserted() { return !m_wstrNodeType.empty(); }
    const wstring& GetNodeType() { return m_wstrNodeType; }
	// SOAP-ENV attributes
	wstring& GetId() { return m_wstrId; }
	wstring& GetHref() { return m_wstrHref; }
	bool IsRef() { return !m_wstrHref.empty(); }

	void SetArrayOffsets(const wstring& wstrOffsets)
	{
		m_wstrOffset = wstrOffsets;
		wstring::size_type pos;
		pos = wstrOffsets.rfind(wchar_t('['));
		if (pos != wstring::npos)
		{
			// break out comma separated numeric values
			wstring wstrTemp = wstrOffsets.substr(++pos);
			long lOffset = _wtol(wstrTemp.c_str());
			m_arrArrayOffsets.push_back(lOffset);
			while ((pos = wstrTemp.find(wchar_t(','))) != wstring::npos)
			{
				wstrTemp = wstrTemp.substr(++pos);
				long l = _wtol(wstrTemp.c_str());
				if (l != 0)
				{
					// offset supported only for innermost dimension in multi-dim array
					goto SOAPFault;
				}
				m_arrArrayOffsets.push_back(l);
			}
		}
		else
		{
			// bad syntax
			goto SOAPFault;
		}
		// sanity checks if this is not a multi-ref
		if (IsRef() == true)
		{
			return;
		}
		if (m_arrArrayOffsets.size() != m_arrArrayDims.size())
		{
			// dimensionality inconsistent
			goto SOAPFault;
		}
		// was asize fully specified?
		if (m_lNumElems == -1)
		{
			// no, we are done
			return;
		}
		// yes, we will recalculate the total number of array elems transmitted
		m_lNumElems = 1;
		unsigned int i;
		for (i = 0; i < m_arrArrayOffsets.size(); i++)
		{
			long lSize = m_arrArrayDims[i] - m_arrArrayOffsets[i];
			if (lSize < 0)
			{
				// offset greater > upper bound
				// implies negative nbr elements
				goto SOAPFault;
			}
			m_lNumElems *= lSize;
		}
		return;

SOAPFault:
		// array size not given
		SOAPFault(WMSOAPFault::Sender, L"Invalid value for attribute 'offset' or 'arrayType' on element.", true);
	}

	void SetArrayDataType(const wstring& wstrType, NSPrefixMap& mapNSPrefixes)
	{
        m_arrArrayDims.clear();
		wstring wstrTemp;
		wstring::size_type pos;
		NSPrefixMap::iterator iNS;
		if ((pos = wstrType.find(wchar_t(':'))) != wstring::npos)
		{
			// get NS prefix
			if ((iNS = mapNSPrefixes.find(wstrType.substr(0, pos))) != mapNSPrefixes.end())
			{
				m_wstrArrayElemTypeNS = (*iNS).second;
				wstrTemp = wstrType.substr(++pos);
			}
			else
			{
				// namespace not found in NS map
				SOAPFault(WMSOAPFault::Sender,
						L"Encountered undeclared namespace prefix for attribute 'arrayType' on element.", true);
			}
		}
		else
		{
			// default namespace
			if ((iNS = mapNSPrefixes.find(wstring(L""))) != mapNSPrefixes.end())
			{
				m_wstrArrayElemTypeNS = (*iNS).second;
				wstrTemp = m_wstrType;
			}
			else
			{
				SOAPFault(WMSOAPFault::Sender,
						L"Encountered unresolvable namespace for attribute 'arrayType' on element.", true);
			}
		}
		// get array size
		if ((pos = wstrTemp.rfind(wchar_t('['))) != wstring::npos)
		{
			m_wstrArraySize = wstrTemp.substr(pos);
		}
		else
		{
			// array size not given
			SOAPFault(WMSOAPFault::Sender,
					L"Value of attribute 'arrayType' on element doesn't have size given.", true);
		}
		m_wstrArrayElemType = wstrTemp.substr(0, pos);
		// process asize... skip first char ('[')
		wstrTemp = m_wstrArraySize.substr(1);
		bool bAsizeEmptyDim = false;
		if (!iswdigit(wstrTemp[0]))
		{
			// empty asize for innermost dimension
			bAsizeEmptyDim = true;
		}
		// break out comma separated integer values
		m_lNumElems = _wtol(wstrTemp.c_str());
		m_arrArrayDims.push_back(m_lNumElems);
		while ((pos = wstrTemp.find(wchar_t(','))) != wstring::npos)
		{
			wstrTemp = wstrTemp.substr(++pos);
			if (!iswdigit(wstrTemp[0]))
			{
				// empty size supported only for innermost dimension
				SOAPFault(WMSOAPFault::Sender,
						L"Value of attribute 'arrayType' on element is invalid, size may be unspecified for innermost dimension only.", true);
			}
			m_arrArrayDims.push_back(_wtol(wstrTemp.c_str()));
			m_lNumElems *= m_arrArrayDims.back();
		}
		if (bAsizeEmptyDim == true)
		{
			// special value to indicate unspecified size for innermost dimension
			m_lNumElems = -1;
		}
	}

    void SetItemType(const wstring& wstrItemType, NSPrefixMap& mapNSPrefixes)
    {
        // parses SOAP 1.2 itemType attribute
		wstring::size_type pos;
		NSPrefixMap::iterator iNS;
		if ((pos = wstrItemType.find(wchar_t(':'))) != wstring::npos)
		{
			// get NS prefix
			if ((iNS = mapNSPrefixes.find(wstrItemType.substr(0, pos))) != mapNSPrefixes.end())
			{
				m_wstrArrayElemTypeNS = (*iNS).second;
				m_wstrArrayElemType = wstrItemType.substr(++pos);
			}
			else
			{
				// namespace not found in NS map
				SOAPFault(WMSOAPFault::Sender,
						L"Encountered undeclared namespace prefix for attribute 'itemType' on element.", true);
			}
		}
		else
		{
			// default namespace
			if ((iNS = mapNSPrefixes.find(wstring(L""))) != mapNSPrefixes.end())
			{
				m_wstrArrayElemTypeNS = (*iNS).second;
				m_wstrArrayElemType = m_wstrType;
			}
			else
			{
				SOAPFault(WMSOAPFault::Sender,
						L"Encountered unresolvable namespace for attribute 'itemType' on element.", true);
			}
		}
    }

    void SetArraySize(const wstring& wstrArraySize)
    {
        m_arrArrayDims.clear();
        // parses SOAP 1.2 arraySize attribute
		wstring::size_type pos;
        wstring wstrTemp = wstrArraySize;
		// process asize...
		bool bAsizeEmptyDim = false;
		if (wstrTemp[0] == wchar_t('*'))
		{
			// empty asize for innermost dimension
			bAsizeEmptyDim = true;
		}
		// break out space separated integer values
		m_lNumElems = _wtol(wstrTemp.c_str());
		m_arrArrayDims.push_back(m_lNumElems);
		while ((pos = wstrTemp.find(wchar_t(' '))) != wstring::npos)
		{
			wstrTemp = wstrTemp.substr(++pos);
			if (wstrTemp[0] == wchar_t('*'))
			{
				// empty size supported only for innermost dimension
				SOAPFault(WMSOAPFault::Sender,
						L"Value of attribute 'arrayType' on element is invalid, size may be unspecified for innermost dimension only.", true);
			}
			m_arrArrayDims.push_back(_wtol(wstrTemp.c_str()));
			m_lNumElems *= m_arrArrayDims.back();
		}
		if (bAsizeEmptyDim == true)
		{
			// special value to indicate unspecified size for innermost dimension
			m_lNumElems = -1;
		}
    }

	bool CheckEncodingStyle(const wstring& wstrStyle)
    { 
        if (GetEncodingStyle().empty() == false &&
            GetEncodingStyle() != wstrStyle)
        {
            return false;
        }
        return true;
    }

protected:

	wstring				m_wstrId;
	wstring				m_wstrHref;
	wstring				m_wstrRoot;
    wstring             m_wstrNodeType;

	// xsi attributes
	wstring				m_wstrType;
	wstring				m_wstrTypeNS;
	wstring				m_wstrNil;

	// array attributes
	wstring				m_wstrOffset;
	wstring				m_wstrArraySize;
	vector<long>		m_arrArrayDims;
	vector<long>		m_arrArrayOffsets;
	long				m_lNumElems;
	wstring				m_wstrArrayElemType;
	wstring				m_wstrArrayElemTypeNS;

	// character content... used by simple params
	wstring				m_wstrCharContent;

	// list of child elements... used by compound params
	vector<WMSOAPParamHandler *> m_arrChildren;

	// multi-ref value map pointer passed in ctor
	map<wstring, WMSOAPParamHandler	*>	*m_pmapMultiRefElems;
};

class WMSOAPFaultCodeHandler : public WMSOAPEventHandler
{
public:
    WMSOAPFaultCodeHandler() {}

	void Characters(const wstring& wstrChars) { m_wstrContent = wstrChars; }

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement) { return IGNORE_ELEMENT_HANDLER; }

    bool EndElement(const wstring& wstrURI,
					const wstring& wstrLocalName,
					const wstring& wstrQualifiedName,
					WMXMLElement *pElement)
	{
		// resolve qname
		wstring wstrTemp = m_wstrContent;
		wstring::size_type pos = wstrTemp.find(':');
		if (pos != wstring::npos)
		{
			// get NS prefix
			NSPrefixMap::iterator iNS;
            if ((iNS = pElement->GetNSPrefixMap().find(wstrTemp.substr(0, pos))) != pElement->GetNSPrefixMap().end())
			{
				m_wstrValueNS = (*iNS).second;
			}
			else
			{
				// unresolvable namespace prefix
				SOAPFault(WMSOAPFault::Sender,
						L"Encountered undeclared namespace prefix for value of fault code element in Fault message.", true);
			}
			m_wstrValue = wstrTemp.substr(++pos);
		}
		else
		{
			// default namespace
			NSPrefixMap::iterator iNS;
			if ((iNS = pElement->GetNSPrefixMap().find(wstring(L""))) != pElement->GetNSPrefixMap().end())
			{
				m_wstrValueNS = (*iNS).second;
			}
			else
			{
				// unresolvable namespace prefix
				SOAPFault(WMSOAPFault::Sender,
					L"Unresolvable namespace for value of fault code element in Fault message.", true);
			}
			m_wstrValue = wstrTemp;
		}
        return true;
    }
    const wstring& GetValue() { return m_wstrValue; }
    const wstring& GetValueNS() { return m_wstrValueNS; }

private:
    wstring                     m_wstrContent;
	wstring                     m_wstrValue;
    wstring                     m_wstrValueNS;
};

class WMSOAP12FaultSubcodeHandler : public WMSOAPElemHandler
{
public:
	WMSOAP12FaultSubcodeHandler() {}
    ~WMSOAP12FaultSubcodeHandler(){}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement)
    {
		if (wstrLocalName == L"Value" && wstrURI == g_pwszSoap12EnvelopeNS)
		{
			return &m_Code;
		}
		return IGNORE_ELEMENT_HANDLER;
    }
    const wstring& GetValue() { return m_Code.GetValue(); }
    const wstring& GetValueNS() { return m_Code.GetValueNS(); }

private:
    WMSOAPFaultCodeHandler      m_Code;
};

class WMSOAP12FaultCodeHandler : public WMSOAPElemHandler
{
public:
	WMSOAP12FaultCodeHandler() {}
    ~WMSOAP12FaultCodeHandler() {}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement)
    {
		if (wstrLocalName == L"Value" && wstrURI == g_pwszSoap12EnvelopeNS)
		{
			return &m_Code;
		}
		if (wstrLocalName == L"Subcode" && wstrURI == g_pwszSoap12EnvelopeNS)
		{
			return &m_Subcode;
		}
		return IGNORE_ELEMENT_HANDLER;
    }
    const wstring& GetValue() { return m_Code.GetValue(); }
    const wstring& GetValueNS() { return m_Code.GetValueNS(); }
    const wstring& GetSubcodeValue()
    {
        return m_Subcode.GetValue();
    }
    const wstring& GetSubcodeValueNS()
    {
        return m_Subcode.GetValueNS();
    }

private:
    WMSOAPFaultCodeHandler          m_Code;
    WMSOAP12FaultSubcodeHandler     m_Subcode;
};

class WMSOAP12FaultReasonTextHandler : public WMSOAPFaultCodeHandler
{
public:
    WMSOAP12FaultReasonTextHandler(map<wstring, wstring>& mapTextElems) : m_mapTextElems(mapTextElems) {}
    ~WMSOAP12FaultReasonTextHandler(){}

    virtual void ReadAttributes(WMXMLAttributeMap& mapAttributes, NSPrefixMap& mapNSPrefixes)
	{
		wstring wstrKey(L"http://www.w3.org/XML/1998/namespace");
		wstrKey += L"lang";
		WMXMLAttributeMap::iterator iter;
		if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		{
			m_wstrLangId = (*iter).second->GetValue();
		}
	}
    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement) { return IGNORE_ELEMENT_HANDLER; }

	bool EndElement(const wstring& wstrURI,
					const wstring& wstrLocalName,
					const wstring& wstrQualifiedName,
					WMXMLElement *pElement)
    { 
        m_mapTextElems[m_wstrLangId] = m_wstrText;
        delete this;
        return true;
    }
	void Characters(const wstring& wstrChars) { m_wstrText = wstrChars; }

private:
    map<wstring, wstring>&  m_mapTextElems;
    wstring                 m_wstrText;
    wstring                 m_wstrLangId;
};

class WMSOAP12FaultReasonHandler : public WMSOAPElemHandler
{
public:
	WMSOAP12FaultReasonHandler() {}
    ~WMSOAP12FaultReasonHandler() {}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement)
    {
		if (wstrLocalName == L"Text" && wstrURI == g_pwszSoap12EnvelopeNS)
		{
			WMSOAP12FaultReasonTextHandler *pText = new WMSOAP12FaultReasonTextHandler(m_mapTextElems);
			pText->ReadAttributes(mapAttributes, mapNSPrefixes);
            return pText;
		}
		return IGNORE_ELEMENT_HANDLER;
    }
    bool GetText(const wstring& wstrLang, wstring& wstrText)
    {
        map<wstring, wstring>::iterator iM;
        if ((iM = m_mapTextElems.find(wstrLang)) != m_mapTextElems.end())
        {
            wstrText = (*iM).second;
            return true;
        }
        return false;
    }
    map<wstring, wstring>& GetTextMap() { return m_mapTextElems; }

private:
    map<wstring, wstring>      m_mapTextElems;
};

class WMSOAPFaultHandler : public WMSOAPParamHandler
{
public:
	WMSOAPFaultHandler( const wstring& wstrURI,
						const wstring& wstrLocalName,
						map<wstring, WMSOAPParamHandler	*>	*pmapMultiRefElems) :
						WMSOAPParamHandler(wstrURI, wstrLocalName, pmapMultiRefElems)
	{ m_pDetails = NULL; }
	virtual ~WMSOAPFaultHandler()
	{
		if (m_pDetails != NULL )
		{
			delete m_pDetails;
		}
	}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement)
	{
        if (GetSOAPVersion() == SOAP_VERSION_1_1)
        {
		    if (wstrLocalName == L"faultcode")
		    {
			    return &m_FaultCode;
		    }
		    if (wstrLocalName == L"faultstring")
		    {
			    return (new WMSOAPCharacterHandler(m_wstrFaultString));
		    }
		    if (wstrLocalName == L"faultactor")
		    {
			    return (new WMSOAPCharacterHandler(m_wstrFaultActor));
		    }
		    if (wstrLocalName == L"detail")
		    {
			    if (m_pDetails != NULL)
			    {
				    return IGNORE_ELEMENT_HANDLER;
			    }
			    m_pDetails = new WMSOAPParamHandler(wstrURI,
												    wstrLocalName,
												    m_pmapMultiRefElems);
                m_pDetails->SetSOAPVersion(m_nSOAPVersion);
			    m_pDetails->SetEncodingStyle(GetEncodingStyle());
			    m_pDetails->ReadAttributes(mapAttributes, mapNSPrefixes);
			    return m_pDetails;
		    }
        }
        else // SOAP 1.2
        {
		    if (wstrLocalName == L"Code" && wstrURI == g_pwszSoap12EnvelopeNS)
		    {
			    return &m_Code;
		    }
		    if (wstrLocalName == L"Reason" && wstrURI == g_pwszSoap12EnvelopeNS)
		    {
                return &m_Reason;
		    }
		    if (wstrLocalName == L"Node" && wstrURI == g_pwszSoap12EnvelopeNS)
		    {
			    return (new WMSOAPCharacterHandler(m_wstrNode));
		    }
		    if (wstrLocalName == L"Role" && wstrURI == g_pwszSoap12EnvelopeNS)
		    {
			    return (new WMSOAPCharacterHandler(m_wstrRole));
		    }
		    if (wstrLocalName == L"Detail" && wstrURI == g_pwszSoap12EnvelopeNS)
		    {
			    if (m_pDetails != NULL)
			    {
				    return IGNORE_ELEMENT_HANDLER;
			    }
			    m_pDetails = new WMSOAPParamHandler(wstrURI,
												    wstrLocalName,
												    m_pmapMultiRefElems);
                m_pDetails->SetSOAPVersion(m_nSOAPVersion);
			    m_pDetails->ReadAttributes(mapAttributes, mapNSPrefixes);
			    return m_pDetails;
		    }
        }
		return IGNORE_ELEMENT_HANDLER;
	}
    const wstring& GetFaultCode() { return m_FaultCode.GetValue(); }
    const wstring& GetFaultCodeNS() { return m_FaultCode.GetValueNS(); }
    const wstring& GetFaultString() { return m_wstrFaultString; }
    const wstring& GetFaultActor() { return m_wstrFaultActor; }
	WMSOAPParamHandler *GetDetails() { return m_pDetails; }

    const wstring& GetCode() { return m_Code.GetValue(); }
    const wstring& GetCodeNS() { return m_Code.GetValueNS(); }
    const wstring& GetSubcode() { return m_Code.GetSubcodeValue(); }
    const wstring& GetSubcodeNS() { return m_Code.GetSubcodeValueNS(); }
    const wstring& GetReason() { return m_wstrReason; }
    map<wstring, wstring>& GetReasonTextMap() { return m_Reason.GetTextMap(); }
    bool GetText(const wstring& wstrLang, wstring& wstrText)
    {
        return m_Reason.GetText(wstrLang, wstrText);
    }
    const wstring& GetNode() { return m_wstrNode; }
    const wstring& GetRole() { return m_wstrRole; }

private:

	WMSOAPFaultCodeHandler      m_FaultCode;
	wstring                     m_wstrFaultCodeNS;
	wstring				        m_wstrFaultString;
	wstring				        m_wstrFaultActor;
	WMSOAPParamHandler	        *m_pDetails;

	WMSOAP12FaultCodeHandler    m_Code;
	wstring				        m_wstrReason;
	wstring				        m_wstrNode;
	wstring				        m_wstrRole;
    WMSOAP12FaultReasonHandler  m_Reason;
};

class WMSOAPHeaderElemHandler : public WMSOAPParamHandler
{
public:
	WMSOAPHeaderElemHandler(const wstring& wstrURI,
							const wstring& wstrLocalName,
							map<wstring, WMSOAPParamHandler	*>	*pmapMultiRefElems) :
				  WMSOAPParamHandler(wstrURI, wstrLocalName, pmapMultiRefElems)
	{
		m_bMustUnderstand = false;
        m_bRelay = false;
	}

	virtual ~WMSOAPHeaderElemHandler() {}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement)
	{
        // process child
		WMSOAPParamHandler *pElemHandler = new WMSOAPParamHandler(  wstrURI,
																	wstrLocalName,
																	m_pmapMultiRefElems);
		m_arrChildren.push_back(pElemHandler);
        pElemHandler->SetSOAPVersion(m_nSOAPVersion);
        pElemHandler->SetEncodingStyle(GetEncodingStyle());
		pElemHandler->ReadAttributes(mapAttributes, mapNSPrefixes);
		// does it have id attribute?
		if (pElemHandler->GetId().empty() == false)
		{
			// yes, so place in multi-ref map
            if (m_pmapMultiRefElems->find(pElemHandler->GetId()) == m_pmapMultiRefElems->end())
            {
			    m_pmapMultiRefElems->insert(map<wstring, WMSOAPParamHandler *>::value_type(pElemHandler->GetId(), pElemHandler));
            }
            else
            {
                if (m_nSOAPVersion == SOAP_VERSION_1_2)
                {
			        SOAPFault(WMSOAPFault::Sender,
                                L"DuplicateID",
                                g_pwszSoap12EncodingNS,
						        L"Id value found on accessor element is a duplicate.",
                                false);
		        }
			    SOAPFault(WMSOAPFault::Sender,
						    L"Id value found on accessor element is a duplicate.",
                            false);
            }
		}
		return pElemHandler;
	}

	bool EndElement(const wstring& wstrURI,
					const wstring& wstrLocalName,
					const wstring& wstrQualifiedName,
					WMXMLElement *pElement)
	{
		SetXMLElement(pElement);
		wstring wstrValue;
		if (GetXMLElement()->GetAttribute(  wstring(g_pwszSoapEnvelopeNS),
											wstring(L"encodingStyle"),
											wstrValue) == false)
		{
			if (GetEncodingStyle().empty() == false)
			{
                // this element may have been in the scope of an encodingStyle
                // declaration at the soap:Header or soap:Envelope level if the 
                // SOAP version is 1.1. If so, adorn the header block element with a
                // similar attribute in case it needs to be reserialized.
                if (m_nSOAPVersion == SOAP_VERSION_1_1)
                {
				    GetXMLElement()->SetAttribute(  wstring(L"SOAP-ENV:encodingStyle"),
												    wstring(g_pwszSoapEnvelopeNS),
												    wstring(GetEncodingStyle()));
                }
			}
		}
		return true;
	}

    void ReadAttributes(WMXMLAttributeMap& mapAttributes, NSPrefixMap& mapNSPrefixes)
	{
		WMSOAPParamHandler::ReadAttributes(mapAttributes, mapNSPrefixes);
		// look for 'SOAP-ENV:mustUnderstand attribute
        wstring wstrKey;
        if (m_nSOAPVersion == SOAP_VERSION_1_1)
        {
		    wstrKey = g_pwszSoapEnvelopeNS;
        }
        else
        {
		    wstrKey = g_pwszSoap12EnvelopeNS;
        }
		wstrKey += L"mustUnderstand";
		WMXMLAttributeMap::iterator iter;
		if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		{
			if ((*iter).second->GetValue() == L"1" || (*iter).second->GetValue() == L"true")
			{
				m_bMustUnderstand = true;
			}
			else if ((*iter).second->GetValue() != L"0" && (*iter).second->GetValue() != L"false")
			{
                // invalid
				SOAPFault(WMSOAPFault::Sender,
						L"Encountered invalid value for attribute 'mustUnderstand' on header block.", false);
			}
		}
	    // check for any 'SOAP-ENV:actor' attribute
        if (m_nSOAPVersion == SOAP_VERSION_1_1)
        {
		    wstrKey = g_pwszSoapEnvelopeNS;
		    wstrKey += L"actor";
        }
        else
        {
            // changed to 'role' in SOAP 1.2
		    wstrKey = g_pwszSoap12EnvelopeNS;
		    wstrKey += L"role";
        }
		if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		{
			m_wstrActorURI = (*iter).second->GetValue();
            if (m_wstrActorURI == g_pwszSoap12RoleUltimate)
            {
                // ultimate receiver role, empty string used internally
                m_wstrActorURI = L"";
            }
            else if (m_wstrActorURI.empty() == true)
            {
                // role value is empty URI, populate with ""
                m_wstrActorURI = L"\"\"";
            }
		}
	    // check for any SOAP 1.2 'SOAP-ENV:relay' attribute
        if (m_nSOAPVersion == SOAP_VERSION_1_2)
        {
		    wstrKey = g_pwszSoap12EnvelopeNS;
		    wstrKey += L"relay";
		    WMXMLAttributeMap::iterator iter;
		    if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
		    {
			    if ((*iter).second->GetValue() == L"1" || (*iter).second->GetValue() == L"true")
			    {
				    m_bRelay = true;
			    }
			    else if ((*iter).second->GetValue() != L"0" && (*iter).second->GetValue() != L"false")
			    {
                    // invalid
				    SOAPFault(WMSOAPFault::Sender,
						    L"Encountered invalid value for attribute 'relay' on header block.", false);
			    }
		    }
        }
	}

	bool IsMustUnderstand() { return m_bMustUnderstand; }
    bool IsRelayable() { return m_bRelay; }
	const wstring& GetActorURI() { return m_wstrActorURI; }
	void SetActorURI(const wstring& wstrURI) { m_wstrActorURI = wstrURI; }
	const wstring& GetRoleURI() { return m_wstrActorURI; }
	void SetRoleURI(const wstring& wstrURI) { m_wstrActorURI = wstrURI; }

private:
	bool	m_bMustUnderstand;
    bool    m_bRelay;
	wstring	m_wstrActorURI;
};

class WMSOAPHeaderHandler : public WMSOAPElemHandler
{
public:
	WMSOAPHeaderHandler() {}
	virtual ~WMSOAPHeaderHandler()
	{
		vector<WMSOAPHeaderElemHandler *>::iterator iV;
		for (iV = m_arrEntries.begin();
			iV != m_arrEntries.end(); iV++)
		{
			delete *iV;
		}
	}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement)
	{
        if (wstrURI.empty() == true)
        {
			// children of Body must be namespace qualified
			SOAPFault(WMSOAPFault::Sender,
					L"Encountered SOAP Header Block with unqualified name.", false);
        }
		WMSOAPHeaderElemHandler *pHeaderElem = new WMSOAPHeaderElemHandler( wstrURI,
																			wstrLocalName,
																			&m_mapMultiRefElems);
		m_arrEntries.push_back(pHeaderElem);
        pHeaderElem->SetSOAPVersion(m_nSOAPVersion);
        if (m_nSOAPVersion == SOAP_VERSION_1_1)
        {
    		pHeaderElem->SetEncodingStyle(GetEncodingStyle());
        }
		pHeaderElem->ReadAttributes(mapAttributes, mapNSPrefixes);
		// does it have id attribute?
		if (pHeaderElem->GetId().empty() == false)
		{
			// yes, so place in multi-ref map
			m_mapMultiRefElems.insert(map<wstring, WMSOAPParamHandler *>::value_type(pHeaderElem->GetId(), pHeaderElem));
		}
		return pHeaderElem;
	}

	const vector<WMSOAPHeaderElemHandler *>& GetHeaderEntries() { return m_arrEntries; }

	WMSOAPParamHandler *GetMultiRefElem(wstring wstrID)
	{
		map<wstring, WMSOAPParamHandler	*>::iterator iter;
		iter = m_mapMultiRefElems.find(wstrID);
		if (iter != m_mapMultiRefElems.end())
		{
			return (*iter).second;
		}
		return NULL;
	}

private:
	// header entry elements
	vector<WMSOAPHeaderElemHandler *> m_arrEntries;
	// multi-ref value elements map
	map<wstring, WMSOAPParamHandler	*>	m_mapMultiRefElems;
};

class WMSOAPRequestHandler : public WMSOAPParamHandler
{
public:
	WMSOAPRequestHandler(   const wstring& wstrURI,
							const wstring& wstrLocalName,
							map<wstring, WMSOAPParamHandler	*>	*pmapMultiRefElems) :
							WMSOAPParamHandler(wstrURI, wstrLocalName, pmapMultiRefElems)
	{}

	virtual ~WMSOAPRequestHandler()	{}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement)
	{
		WMSOAPParamHandler *pParam = new WMSOAPParamHandler(wstrURI,
															wstrLocalName,
															m_pmapMultiRefElems);
		m_arrChildren.push_back(pParam);
        pParam->SetSOAPVersion(m_nSOAPVersion);
        pParam->SetEncodingStyle(GetEncodingStyle());
		pParam->ReadAttributes(mapAttributes, mapNSPrefixes);
		// does it have id attribute?
		if (pParam->GetId().empty() == false)
		{
			// yes, so place in multi-ref map
            if (m_pmapMultiRefElems->find(pParam->GetId()) == m_pmapMultiRefElems->end())
            {
			    m_pmapMultiRefElems->insert(map<wstring, WMSOAPParamHandler *>::value_type(pParam->GetId(), pParam));
            }
            else
            {
                if (m_nSOAPVersion == SOAP_VERSION_1_2)
                {
			        SOAPFault(WMSOAPFault::Sender,
                                L"DuplicateID",
                                g_pwszSoap12EncodingNS,
						        L"Id value found on accessor element is a duplicate.",
                                true);
		        }
			    SOAPFault(WMSOAPFault::Sender,
						    L"Id value found on accessor element is a duplicate.",
                            true);
            }
		}
		return pParam;
	}

	const vector<WMSOAPParamHandler *>& GetParameters() { return m_arrChildren; }
};

class WMSOAPResultElemHandler : public WMSOAPEventHandler
{
public:
    WMSOAPResultElemHandler() {}

	void Characters(const wstring& wstrChars) { m_wstrQName = wstrChars; }

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement) { return IGNORE_ELEMENT_HANDLER; }

    bool EndElement(const wstring& wstrURI,
					const wstring& wstrLocalName,
					const wstring& wstrQualifiedName,
					WMXMLElement *pElement)
	{
		// resolve qname
		wstring wstrTemp = m_wstrQName;
		wstring::size_type pos = wstrTemp.find(':');
		if (pos != wstring::npos)
		{
			// get NS prefix
			NSPrefixMap::iterator iNS;
            if ((iNS = pElement->GetNSPrefixMap().find(wstrTemp.substr(0, pos))) != pElement->GetNSPrefixMap().end())
			{
				m_wstrNS = (*iNS).second;
			}
			else
			{
				// unresolvable namespace prefix
				SOAPFault(WMSOAPFault::Sender,
						L"Encountered undeclared namespace prefix for QName in RPC 'result' element.", true);
			}
			m_wstrName = wstrTemp.substr(++pos);
		}
		else
		{
			m_wstrName = wstrTemp;
		}
        return true;
    }
    const wstring& GetName() { return m_wstrName; }
    const wstring& GetNS() { return m_wstrNS; }

private:
    wstring                     m_wstrQName;
	wstring                     m_wstrName;
    wstring                     m_wstrNS;
};

class WMSOAPResponseHandler : public WMSOAPParamHandler
{
public:
	WMSOAPResponseHandler(  const wstring& wstrURI,
							const wstring& wstrLocalName,
							map<wstring, WMSOAPParamHandler	*>	*pmapMultiRefElems) :
							WMSOAPParamHandler(wstrURI, wstrLocalName, pmapMultiRefElems)
	{ m_bResultElemFound = false; }
	virtual ~WMSOAPResponseHandler() {}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement)
	{
        if (m_nSOAPVersion == SOAP_VERSION_1_2)
        {
            // is this the result element?
            if (wstrLocalName == L"result" && wstrURI == g_pwszSoap12RpcNS)
            {
                m_bResultElemFound = true;
    			return &m_ResultHandler;
            }
        }
		WMSOAPParamHandler *pParam = new WMSOAPParamHandler(wstrURI,
															wstrLocalName,
															m_pmapMultiRefElems);
		m_arrChildren.push_back(pParam);
        pParam->SetSOAPVersion(m_nSOAPVersion);
        pParam->SetEncodingStyle(GetEncodingStyle());
		pParam->ReadAttributes(mapAttributes, mapNSPrefixes);
		// does it have id attribute?
		if (pParam->GetId().empty() == false)
		{
			// yes, so place in multi-ref map
            if (m_pmapMultiRefElems->find(pParam->GetId()) == m_pmapMultiRefElems->end())
            {
			    m_pmapMultiRefElems->insert(map<wstring, WMSOAPParamHandler *>::value_type(pParam->GetId(), pParam));
            }
            else
            {
                if (m_nSOAPVersion == SOAP_VERSION_1_2)
                {
			        SOAPFault(WMSOAPFault::Sender,
                                L"DuplicateID",
                                g_pwszSoap12EncodingNS,
						        L"Id value found on accessor element is a duplicate.",
                                true);
		        }
			    SOAPFault(WMSOAPFault::Sender,
						    L"Id value found on accessor element is a duplicate.",
                            true);
            }
		}
		return pParam;
	}

	const vector<WMSOAPParamHandler *>& GetParameters() { return m_arrChildren; }
    const wstring& GetRetValName() { return m_ResultHandler.GetName(); }
    const wstring& GetRetValNS() { return m_ResultHandler.GetNS(); }
    bool ResultElemFound() { return m_bResultElemFound; }

private:
    WMSOAPResultElemHandler m_ResultHandler;
    bool                    m_bResultElemFound;
};

class WMSOAPRequestBodyHandler : public WMSOAPElemHandler
{
public:
	WMSOAPRequestBodyHandler()
	{
		m_pRequest = NULL;
	}
	virtual ~WMSOAPRequestBodyHandler()
	{ 
		// release request object
		if (m_pRequest != NULL) delete m_pRequest;
		// empty independent element list
		list<WMSOAPParamHandler *>::iterator iL;
		for (iL = m_listIndependentElems.begin();
			iL != m_listIndependentElems.end(); iL++)
		{
			delete *iL;
		}
	}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement)
	{
        if (m_nSOAPVersion == SOAP_VERSION_1_1)
        {
		    // has serialization root been found?
		    if (m_pRequest == NULL)
		    {
			    // check to see if SOAP-ENC:root is "0"
			    wstring wstrKey = g_pwszSoapEncodingNS;
			    wstrKey += L"root";
			    WMXMLAttributeMap::iterator iter;
			    if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
			    {
				    if ((*iter).second->GetValue() == L"0")
				    {
					    // not serialization root, so store as multi-ref element
					    goto ReqMultiref;
				    }
			    }
			    // ok, it's root (SOAP section 7 RPC "wrapper element")
			    m_pRequest = new WMSOAPRequestHandler(wstrURI, wstrLocalName, &m_mapMultiRefElems);
                m_pRequest->SetSOAPVersion(m_nSOAPVersion);
			    m_pRequest->SetEncodingStyle(GetEncodingStyle());
			    m_pRequest->ReadAttributes(mapAttributes, mapNSPrefixes);
			    return m_pRequest;
		    }
    ReqMultiref:
		    // not serialization root, so store as independent element
		    WMSOAPParamHandler *pParam = new WMSOAPParamHandler(wstrURI,
															    wstrLocalName,
															    &m_mapMultiRefElems);
		    m_listIndependentElems.push_back(pParam);
            pParam->SetSOAPVersion(m_nSOAPVersion);
		    pParam->SetEncodingStyle(GetEncodingStyle());
		    pParam->ReadAttributes(mapAttributes, mapNSPrefixes);
		    // does it have id attribute?
		    if (pParam->GetId().empty() == false)
		    {
			    // yes, so place in multi-ref map
			    m_mapMultiRefElems.insert(map<wstring, WMSOAPParamHandler *>::value_type(pParam->GetId(), pParam));
		    }
		    return pParam;
        }
        else // SOAP 1.2
        {
		    if (m_pRequest == NULL)
		    {
			    // assume SOAP 1.2 RPC "wrapper element"
			    m_pRequest = new WMSOAPRequestHandler(wstrURI, wstrLocalName, &m_mapMultiRefElems);
                m_pRequest->SetSOAPVersion(m_nSOAPVersion);
			    m_pRequest->ReadAttributes(mapAttributes, mapNSPrefixes);
			    return m_pRequest;
		    }
    		return IGNORE_ELEMENT_HANDLER;
        }
	}

	WMSOAPRequestHandler *GetRequestHandler() { return m_pRequest; }

	WMSOAPParamHandler *GetMultiRefElem(wstring wstrID)
	{
		map<wstring, WMSOAPParamHandler	*>::iterator iter;
		iter = m_mapMultiRefElems.find(wstrID);
		if (iter != m_mapMultiRefElems.end())
		{
			return (*iter).second;
		}
		return NULL;
	}

protected:
	WMSOAPRequestHandler				*m_pRequest;
	list<WMSOAPParamHandler	*>			m_listIndependentElems;
	map<wstring, WMSOAPParamHandler	*>	m_mapMultiRefElems;
};

class WMSOAPResponseBodyHandler : public WMSOAPElemHandler
{
public:
	WMSOAPResponseBodyHandler()
	{
		m_pResponse = NULL;
		m_pSOAPFaultHandler = NULL;
		m_bIsFault = false; 
	}
	virtual ~WMSOAPResponseBodyHandler()
	{
		// release response object
		if (m_pResponse != NULL)
			delete m_pResponse;
		// release fault handler
		if (m_pSOAPFaultHandler != NULL)
			delete m_pSOAPFaultHandler;
		// empty independent element list
		list<WMSOAPParamHandler *>::iterator iL;
		for (iL = m_listIndependentElems.begin();
			iL != m_listIndependentElems.end(); iL++)
		{
			delete *iL;
		}
	}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement)
	{
        if (m_nSOAPVersion == SOAP_VERSION_1_1)
        {
		    // see if it is a Fault element
		    if (wstrURI == g_pwszSoapEnvelopeNS && wstrLocalName == L"Fault")
		    {
			    // has a handler been created yet?
			    if (m_pSOAPFaultHandler == NULL)
			    {
				    m_bIsFault = true;
				    m_pSOAPFaultHandler = new WMSOAPFaultHandler(wstrURI, wstrLocalName, &m_mapMultiRefElems);
                    m_pSOAPFaultHandler->SetSOAPVersion(m_nSOAPVersion);
				    m_pSOAPFaultHandler->SetEncodingStyle(GetEncodingStyle());
				    m_pSOAPFaultHandler->ReadAttributes(mapAttributes, mapNSPrefixes);
				    return m_pSOAPFaultHandler;
			    }
			    return IGNORE_ELEMENT_HANDLER;
		    }
		    // has serialization root been found?
		    if (m_pResponse == NULL)
		    {
			    // check to see if SOAP-ENC:root is "0"
			    wstring wstrKey = g_pwszSoapEncodingNS;
			    wstrKey += L"root";
			    WMXMLAttributeMap::iterator iter;
			    if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
			    {
				    if ((*iter).second->GetValue() == L"0")
				    {
					    // not serialization root, so store as multi-ref element
					    goto RespMultiref;
				    }
			    }
			    // ok, it's root (SOAP section 7 RPC "wrapper element")
			    m_pResponse = new WMSOAPResponseHandler(wstrURI, wstrLocalName, &m_mapMultiRefElems);
                m_pResponse->SetSOAPVersion(m_nSOAPVersion);
		        m_pResponse->SetEncodingStyle(GetEncodingStyle());
			    m_pResponse->ReadAttributes(mapAttributes, mapNSPrefixes);
			    return m_pResponse;
		    }
    RespMultiref:
		    // not serialization root, so store as independent element
		    WMSOAPParamHandler *pParam = new WMSOAPParamHandler(wstrURI, wstrLocalName, &m_mapMultiRefElems);
		    m_listIndependentElems.push_back(pParam);
            pParam->SetSOAPVersion(m_nSOAPVersion);
		    pParam->SetEncodingStyle(GetEncodingStyle());
		    pParam->ReadAttributes(mapAttributes, mapNSPrefixes);
		    // does it have id attribute?
		    if (pParam->GetId().empty() == false)
		    {
			    // yes, so place in multi-ref map
			    m_mapMultiRefElems.insert(map<wstring, WMSOAPParamHandler *>::value_type(pParam->GetId(), pParam));
		    }
		    return pParam;
        }
        else // SOAP 1.2
        {
		    // see if it is a Fault element
		    if (wstrURI == g_pwszSoap12EnvelopeNS && wstrLocalName == L"Fault")
		    {
			    // has a handler been created yet?
			    if (m_pSOAPFaultHandler == NULL)
			    {
				    m_bIsFault = true;
				    m_pSOAPFaultHandler = new WMSOAPFaultHandler(wstrURI, wstrLocalName, &m_mapMultiRefElems);
                    m_pSOAPFaultHandler->SetSOAPVersion(m_nSOAPVersion);
				    m_pSOAPFaultHandler->ReadAttributes(mapAttributes, mapNSPrefixes);
				    return m_pSOAPFaultHandler;
			    }
			    return IGNORE_ELEMENT_HANDLER;
		    }
		    if (m_pResponse == NULL)
		    {
			    // assume SOAP 1.2 RPC "wrapper element"
			    m_pResponse = new WMSOAPResponseHandler(wstrURI, wstrLocalName, &m_mapMultiRefElems);
                m_pResponse->SetSOAPVersion(m_nSOAPVersion);
			    m_pResponse->ReadAttributes(mapAttributes, mapNSPrefixes);
			    return m_pResponse;
		    }
    		return IGNORE_ELEMENT_HANDLER;
        }
	}

	WMSOAPResponseHandler *GetResponseHandler() { return m_pResponse; }

    // SOAP Fault
	WMSOAPFaultHandler *GetSOAPFaultHandler() { return m_pSOAPFaultHandler; }
	bool IsSOAPFault() { return m_bIsFault; }

	WMSOAPParamHandler *GetMultiRefElem(wstring wstrID)
	{
		map<wstring, WMSOAPParamHandler	*>::iterator iter;
		iter = m_mapMultiRefElems.find(wstrID);
		if (iter != m_mapMultiRefElems.end())
		{
			return (*iter).second;
		}
		return NULL;
	}

private:
	WMSOAPResponseHandler				*m_pResponse;
	WMSOAPFaultHandler					*m_pSOAPFaultHandler;
	bool								m_bIsFault;
	list<WMSOAPParamHandler	*>			m_listIndependentElems;
	map<wstring, WMSOAPParamHandler	*>	m_mapMultiRefElems;
};

template <class A, class B>
class WMSOAPEnvelopeHandler : public WMSOAPElemHandler 
{
public:
	WMSOAPEnvelopeHandler()
    {
        m_bBodyFound = false;
        m_bHeaderFound = false;
    }
	virtual ~WMSOAPEnvelopeHandler() {}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement)
	{
		if ((wstrURI == g_pwszSoapEnvelopeNS || wstrURI == g_pwszSoap12EnvelopeNS) && wstrLocalName == L"Header")
		{
			if (m_nSOAPVersion == SOAP_VERSION_1_2)
			{
				wstring wstrKey = g_pwszSoap12EnvelopeNS;
				wstrKey += L"encodingStyle";
				WMXMLAttributeMap::iterator iter;
				if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
				{
					// encodingStyle attribute not permitted on Envelope element
					SOAPFault(WMSOAPFault::Sender,
							L"encodingStyle attribute not allowed on SOAP 1.2 Header element.", true);
				}
			}
            m_Header.SetSOAPVersion(m_nSOAPVersion);
            if (m_nSOAPVersion == SOAP_VERSION_1_1)
            {
			    m_Header.SetEncodingStyle(GetEncodingStyle());
            }
			m_Header.ReadAttributes(mapAttributes, mapNSPrefixes);
			m_bHeaderFound = true;
			return &m_Header;
		}
		else if ((wstrURI == g_pwszSoapEnvelopeNS || wstrURI == g_pwszSoap12EnvelopeNS) &&	wstrLocalName == L"Body")
		{
			if (m_nSOAPVersion == SOAP_VERSION_1_2)
			{
				wstring wstrKey = g_pwszSoap12EnvelopeNS;
				wstrKey += L"encodingStyle";
				WMXMLAttributeMap::iterator iter;
				if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
				{
					// encodingStyle not permitted on Envelope element
					SOAPFault(WMSOAPFault::Sender,
							L"encodingStyle attribute not allowed on SOAP 1.2 Body element.", true);
				}
			}
            m_Body.SetSOAPVersion(m_nSOAPVersion);
            if (m_nSOAPVersion == SOAP_VERSION_1_1)
            {
    			m_Body.SetEncodingStyle(GetEncodingStyle());
            }
			m_Body.ReadAttributes(mapAttributes, mapNSPrefixes);
			m_bBodyFound = true;
			return &m_Body;
		}
		else
		{
			if (m_nSOAPVersion == SOAP_VERSION_1_2)
			{
				// only Header and Body may be present as children of Envelope
                SOAPFault(WMSOAPFault::Sender,
						L"Encountered illegal child element within SOAP 1.2 Envelope.", true);
			}
		}
		return IGNORE_ELEMENT_HANDLER;
	}

	bool EndElement(const wstring& wstrURI,
					const wstring& wstrLocalName,
					const wstring& wstrQualifiedName,
					WMXMLElement *pElement)
	{
		if (m_bBodyFound == false)
		{
			// Body must be present as child of Envelope
            SOAPFault(WMSOAPFault::Sender,
					L"Body element not found in SOAP Envelope.", true);
		}
		SetXMLElement(pElement);
		return true;
	}
    
	A& GetHeaderHandler() { return m_Header;}
	B& GetBodyHandler() { return m_Body;}
    bool BodyFound() { return m_bBodyFound; }
    bool HeaderFound() { return m_bHeaderFound; }

private:
	A		m_Header;
	B		m_Body;
	bool	m_bBodyFound;
    bool    m_bHeaderFound;
};

template <class T>
class WMSOAPMessageHandler : public WMSOAPEventHandler
{
public:
    WMSOAPMessageHandler() { m_bEnvelopeFound = false; }
    virtual ~WMSOAPMessageHandler(){}

    WMSOAPEventHandler *StartElement(const wstring& wstrURI,
									const wstring& wstrLocalName,
									const wstring& wstrQualifiedName,
									WMXMLAttributeMap& mapAttributes,
									NSPrefixMap& mapNSPrefixes,
									WMXMLElement *pElement)
	{
		if (wstrLocalName == L"Envelope")
		{
			if (wstrURI == g_pwszSoapEnvelopeNS)
			{
                // SOAP 1.1 envelope
                SetSOAPVersion(SOAP_VERSION_1_1);
            }
			else if (wstrURI == g_pwszSoap12EnvelopeNS)
			{
                // SOAP 1.2 envelope
                SetSOAPVersion(SOAP_VERSION_1_2);
            }
            else
			{
				// version mismatch
				SOAPFault(WMSOAPFault::VersionMismatch, L"SOAP Version Mismatch", false);
			}
			if (m_nSOAPVersion == SOAP_VERSION_1_2)
			{
				wstring wstrKey = g_pwszSoap12EnvelopeNS;
				wstrKey += L"encodingStyle";
				WMXMLAttributeMap::iterator iter;
				if ((iter = mapAttributes.find(wstrKey)) != mapAttributes.end())
				{
					// encodingStyle not permitted on Envelope element
					SOAPFault(WMSOAPFault::Sender,
							L"encodingStyle attribute not allowed on SOAP 1.2 Envelope element.", true);
				}
			}
            m_Envelope.SetSOAPVersion(m_nSOAPVersion);
			m_Envelope.ReadAttributes(mapAttributes, mapNSPrefixes);
			m_bEnvelopeFound = true;
            return &m_Envelope;
		}
		return IGNORE_ELEMENT_HANDLER;
	}
	
	T& GetEnvelopeHandler() { return m_Envelope;}
	bool EnvelopeFound() { return m_bEnvelopeFound; }

private:
	T		m_Envelope;
	bool	m_bEnvelopeFound;
};

#endif // !defined(AFX_SOAPELEMENTS_H__443CE236_34D5_11D4_9E67_00105AABC815__INCLUDED_)
