// WMSOAPSerializer.cpp: implementation of the WMSOAPSerializer 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.
//
//////////////////////////////////////////////////////////////////////
//
// WMSOAPSerializer class - implementation of SOAP message Serialization class
// Creates XML wire representation of SOAP message for transport via HTTP.
// Both "encoded" (SOAP encoding only) or "literal" usage supported.
//  
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "WMSOAPSerializer.h"
#include <comdef.h>
#include "base64.h"
#include "hexcode.h"
#include "tmconv.h"
#include "WMXMLMessage.h"
#include "..\wmwsdlclient\wmwsdlclient.h"
#include "..\wmsoapstruct\wmsoapstruct.h"

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

WMSOAPSerializer::WMSOAPSerializer()
{
	// load VARTYPE map.. first the automation types
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"unsignedByte"), VT_UI1));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"byte"), VT_I1));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"short"), VT_I2));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"unsignedShort"), VT_UI2));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"integer"), VT_I4));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"int"), VT_I4));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"unsignedInt"), VT_UI4));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"long"), VT_I4));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"unsignedLong"), VT_UI4));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"float"), VT_R4));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"double"), VT_R8));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"decimal"), VT_DECIMAL));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"string"), VT_BSTR));
	// XML Schema built-in types that don't map directly to automation types follow
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"anyType"), XT_ANYTYPE));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"ur-type"), XT_ANYTYPE));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"anyURI"), XT_ANYURI));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"date"), XT_DATE));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"time"), XT_TIME));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"timeInstant"), XT_DATETIME));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"dateTime"), XT_DATETIME));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"base64"), XT_BASE64));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"base64Binary"), XT_BASE64));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"hexBinary"), XT_HEXBIN));
	m_mapVarTypes.insert(map<wstring, VARTYPE>::value_type(wstring(L"boolean"), XT_BOOLEAN));
}

WMSOAPSerializer::~WMSOAPSerializer()
{
}

bool WMSOAPSerializer::Serialize(WMSOAPMessage& Msg, Iwmwsdlclient2 *pIWSDLClient)
{
	// create response
	WMXMLElement *pResponseXML;
	if (Msg.IsSOAPFault() == true && Msg.IsLocalFault() == true)
	{
		// locally generated fault message
		pResponseXML = SerializeSOAPFaultMsg(Msg, pIWSDLClient);
	}
	else
	{
		// any others
		pResponseXML = SerializeSOAPMsg(Msg, pIWSDLClient);
	}
	if (pResponseXML == NULL)
	{
		return false;
	}
	m_Parser.SetDocRoot(pResponseXML);
	// serialize... outgoing XML encoded as UTF-8
	char *pszUTF8;
	unsigned long ulLength = m_Parser.SerializeXML_UTF8(&pszUTF8);
	Msg.SetBuffer(pszUTF8, ulLength);
	return true;
}

WMXMLElement *WMSOAPSerializer::SerializeSOAPMsg(WMSOAPMessage& Msg,
												 Iwmwsdlclient2 *pIWSDLClient)
{
    // envelope element
    WMXMLElement *pEnv = NULL;
    if (Msg.IsIntermediary() == true)
    {
        if (Msg.IsRequestPhase() == true)
        {
            // we must preserve certain infoset items of Envelope received, e.g. namespace prefixes
            pEnv = new WMXMLElement(Msg.GetReqDeserializer().GetEnvelopeHandler().GetXMLElement()->GetQualifiedName(),
                    Msg.GetReqDeserializer().GetEnvelopeHandler().GetXMLElement()->GetURI(),
                    Msg.GetReqDeserializer().GetEnvelopeHandler().GetXMLElement()->GetLocalName());
            if (pEnv == NULL)
            {
                return NULL;
            }
	        // copy prefix map
            pEnv->GetNSPrefixMap() = Msg.GetReqDeserializer().GetEnvelopeHandler().GetXMLElement()->GetNSPrefixMap();
	        // copy default ns
	        pEnv->SetDefaultNS(Msg.GetReqDeserializer().GetEnvelopeHandler().GetXMLElement()->GetDefaultNS());
	        // copy attribute list
	        WMXMLAttributeMap::iterator iM;
	        for (iM = Msg.GetReqDeserializer().GetEnvelopeHandler().GetXMLElement()->GetAttributeMap().begin();
		        iM != Msg.GetReqDeserializer().GetEnvelopeHandler().GetXMLElement()->GetAttributeMap().end();
		        iM++)
	        {
		        WMXMLAttribute *pDest = new WMXMLAttribute(*(iM->second));
		        if (pDest != NULL)
		        {
                    pEnv->GetAttributeMap().insert(WMXMLAttributeMap::value_type(iM->first, pDest));
		        }
	        }
        }
        else
        {
            // we must preserve certain infoset items of Envelope received, e.g. namespace prefixes
            pEnv = new WMXMLElement(Msg.GetRespDeserializer().GetEnvelopeHandler().GetXMLElement()->GetQualifiedName(),
                    Msg.GetRespDeserializer().GetEnvelopeHandler().GetXMLElement()->GetURI(),
                    Msg.GetRespDeserializer().GetEnvelopeHandler().GetXMLElement()->GetLocalName());
            if (pEnv == NULL)
            {
                return NULL;
            }
	        // copy prefix map
            pEnv->GetNSPrefixMap() = Msg.GetRespDeserializer().GetEnvelopeHandler().GetXMLElement()->GetNSPrefixMap();
	        // copy default ns
	        pEnv->SetDefaultNS(Msg.GetRespDeserializer().GetEnvelopeHandler().GetXMLElement()->GetDefaultNS());
	        // copy attribute list
	        WMXMLAttributeMap::iterator iM;
	        for (iM = Msg.GetRespDeserializer().GetEnvelopeHandler().GetXMLElement()->GetAttributeMap().begin();
		        iM != Msg.GetRespDeserializer().GetEnvelopeHandler().GetXMLElement()->GetAttributeMap().end();
		        iM++)
	        {
		        WMXMLAttribute *pDest = new WMXMLAttribute(*(iM->second));
		        if (pDest != NULL)
		        {
                    pEnv->GetAttributeMap().insert(WMXMLAttributeMap::value_type(iM->first, pDest));
		        }
	        }
        }
    }
    else
    {
	    pEnv = new WMXMLElement(wstring(L"SOAP-ENV:Envelope"));
	    if (pEnv == NULL)
	    {
            return NULL;
        }
        if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
        {
    		pEnv->SetNSPrefix(wstring(L"SOAP-ENV"), wstring(g_pwszSoapEnvelopeNS));
        }
        else
        {
    		pEnv->SetNSPrefix(wstring(L"SOAP-ENV"), wstring(g_pwszSoap12EnvelopeNS));
        }
    }
    long lPosition;
	variant_t varContent;
    // serialize header
    WMXMLElement *pHeader = NULL;
    if (Msg.IsIntermediary() == true &&
        Msg.IsRequestPhase() == true &&
        Msg.GetReqDeserializer().GetEnvelopeHandler().HeaderFound() == true)
    {
        // create clone of Header element found in request handler
        // we must preserve certain infoset items of Header received,
        // e.g. namespace prefixes
        pHeader = new WMXMLElement(Msg.GetReqDeserializer().GetEnvelopeHandler().GetHeaderHandler().GetXMLElement()->GetQualifiedName(),
                Msg.GetReqDeserializer().GetEnvelopeHandler().GetHeaderHandler().GetXMLElement()->GetURI(),
                Msg.GetReqDeserializer().GetEnvelopeHandler().GetHeaderHandler().GetXMLElement()->GetLocalName());
        if (pHeader == NULL)
		{
			delete pEnv;
			return NULL;
		}
	    // copy prefix map
        pHeader->GetNSPrefixMap() = Msg.GetReqDeserializer().GetEnvelopeHandler().GetHeaderHandler().GetXMLElement()->GetNSPrefixMap();
	    // copy default ns
	    pHeader->SetDefaultNS(Msg.GetReqDeserializer().GetEnvelopeHandler().GetHeaderHandler().GetXMLElement()->GetDefaultNS());
	    // copy attribute list
	    WMXMLAttributeMap::iterator iM;
	    for (iM = Msg.GetReqDeserializer().GetEnvelopeHandler().GetHeaderHandler().GetXMLElement()->GetAttributeMap().begin();
		    iM != Msg.GetReqDeserializer().GetEnvelopeHandler().GetHeaderHandler().GetXMLElement()->GetAttributeMap().end();
		    iM++)
	    {
		    WMXMLAttribute *pDest = new WMXMLAttribute(*(iM->second));
		    if (pDest != NULL)
		    {
                pHeader->GetAttributeMap().insert(WMXMLAttributeMap::value_type(iM->first, pDest));
		    }
	    }
    }
    else if (Msg.IsIntermediary() == true &&
             Msg.IsRequestPhase() == false &&
             Msg.GetRespDeserializer().GetEnvelopeHandler().HeaderFound() == true)
    {
        // create clone of Header element found in response handler
        // we must preserve certain infoset items of Header received,
        // e.g. namespace prefixes
        pHeader = new WMXMLElement(Msg.GetRespDeserializer().GetEnvelopeHandler().GetHeaderHandler().GetXMLElement()->GetQualifiedName(),
                Msg.GetRespDeserializer().GetEnvelopeHandler().GetHeaderHandler().GetXMLElement()->GetURI(),
                Msg.GetRespDeserializer().GetEnvelopeHandler().GetHeaderHandler().GetXMLElement()->GetLocalName());
        if (pHeader == NULL)
		{
			delete pEnv;
			return NULL;
		}
	    // copy prefix map
        pHeader->GetNSPrefixMap() = Msg.GetRespDeserializer().GetEnvelopeHandler().GetHeaderHandler().GetXMLElement()->GetNSPrefixMap();
	    // copy default ns
	    pHeader->SetDefaultNS(Msg.GetRespDeserializer().GetEnvelopeHandler().GetHeaderHandler().GetXMLElement()->GetDefaultNS());
	    // copy attribute list
	    WMXMLAttributeMap::iterator iM;
	    for (iM = Msg.GetRespDeserializer().GetEnvelopeHandler().GetHeaderHandler().GetXMLElement()->GetAttributeMap().begin();
		    iM != Msg.GetRespDeserializer().GetEnvelopeHandler().GetHeaderHandler().GetXMLElement()->GetAttributeMap().end();
		    iM++)
	    {
		    WMXMLAttribute *pDest = new WMXMLAttribute(*(iM->second));
		    if (pDest != NULL)
		    {
                pHeader->GetAttributeMap().insert(WMXMLAttributeMap::value_type(iM->first, pDest));
		    }
	    }
    }
    else if (Msg.GetNumHeaderElems() > 0 || Msg.GetNumHeaderElemsLit() > 0)
    {
        // create new Header element to contain blocks
		pHeader = new WMXMLElement(wstring(L"SOAP-ENV:Header"));
		if (pHeader == NULL)
		{
			delete pEnv;
			return NULL;
		}
    }
	pEnv->AddChildElement(pHeader);
    // now populate Header element with children (header blocks)
	// first get any literal XML header blocks not deserialized
    bool bElemFound = false;
    WMSOAPHeaderMsgElementLit *pHdrElemLit;
	for (bElemFound = Msg.GetFirstHeaderElemLit(&pHdrElemLit,
												lPosition);
			bElemFound == true;
			bElemFound = Msg.GetNextHeaderElemLit(  &pHdrElemLit,
											        lPosition)
	)
	{
		WMXMLElement *pElem = ParseElementLit(pHdrElemLit->m_wstrLiteralXML);
		if (pElem != NULL)
		{
			pHeader->AddChildElement(pElem);
		}
		else
		{
			delete pEnv;
			return NULL;
		}
	}
	// now any deserialized SOAP encoded header blocks
    WMSOAPHeaderMsgElement *pHdrElem;
	for (bElemFound = Msg.GetFirstHeaderElem(   &pHdrElem,
											    lPosition);

			bElemFound == true;
			bElemFound = Msg.GetNextHeaderElem( &pHdrElem,
											    lPosition)
	)
	{
        WMXMLElement *pElem = SerializeHeaderElem(  pHdrElem->m_wstrNamespaceURI,
                                                    pHdrElem->m_wstrName,
                                                    pHdrElem->m_varContent,
                                                    pHdrElem->m_bMustUnderstand,
                                                    pHdrElem->m_wstrActor,
                                                    pHdrElem->m_bRelay,
													Msg,
													pIWSDLClient);
		if (pElem != NULL)
		{
			pHeader->AddChildElement(pElem);
		}
		else
		{
			delete pEnv;
			return NULL;
		}
		varContent.Clear();
	}
    // now the mandatory Body element
	WMXMLElement *pBody = NULL;
	// are we an intermediary or a document style endpoint?
	if (Msg.IsIntermediary() == true || Msg.IsRPCStyle() == false)
	{
		// yes, so use stored body XML
		wstring wstrLiteralXML;
		if (Msg.GetBodyElemLit(wstrLiteralXML) == true)
		{
			pBody = ParseElementLit(wstrLiteralXML);
			if (pBody == NULL)
			{
				delete pEnv;
				return NULL;
			}
		}
		else
		{
			// nothing found, append empty 'Body' element
			pBody = new WMXMLElement(wstring(L"SOAP-ENV:Body"));
			if (pBody == NULL)
			{
				delete pEnv;
				return NULL;
			}
            if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
            {
    		    pBody->SetNSPrefix(wstring(L"SOAP-ENV"), wstring(g_pwszSoapEnvelopeNS));
            }
            else
            {
    		    pBody->SetNSPrefix(wstring(L"SOAP-ENV"), wstring(g_pwszSoap12EnvelopeNS));
            }
		}
		pEnv->AddChildElement(pBody);
        return pEnv;
	}
	// no, we are an RPC endpoint
	pBody = new WMXMLElement(wstring(L"SOAP-ENV:Body"));
	if (pBody != NULL)
	{
		pEnv->AddChildElement(pBody);
	}
	else
	{
		delete pEnv;
		return NULL;
	}
	wstring wstrMsgName = L"m:";
	wstrMsgName += Msg.GetMethodName();
	if (Msg.IsRequestPhase() == false)
	{
		wstrMsgName += L"Response";
	}
	WMXMLElement *pMethod = new WMXMLElement(wstrMsgName);
	if (pMethod == NULL)
	{
		delete pEnv;
		return NULL;
	}
	pMethod->SetNSPrefix(wstring(L"m"), Msg.GetMethodNS());
    // get use and encodingStyle specified in WSDL
	VARIANT_BOOL bResult;
    BSTR bstrEncStyle, bstrUse;
    wstring wstrEncStyle, wstrUse;
    BSTR bstrMethod = SysAllocString(Msg.GetMethodName().c_str());
	if (Msg.IsRequestPhase() == true)
	{
        pIWSDLClient->GetSOAPInputBodyUse(bstrMethod, &bstrUse, &bResult);
        if (bResult == VARIANT_TRUE)
        {
            wstrUse = bstrUse;
            SysFreeString(bstrUse);
        }
        pIWSDLClient->GetSOAPInputBodyEncStyle(bstrMethod, &bstrEncStyle, &bResult);
        if (bResult == VARIANT_TRUE)
        {
            wstrEncStyle = bstrEncStyle;
            SysFreeString(bstrEncStyle);
        }
    }
    else
	{
        pIWSDLClient->GetSOAPOutputBodyUse(bstrMethod, &bstrUse, &bResult);
        if (bResult == VARIANT_TRUE)
        {
            wstrUse = bstrUse;
            SysFreeString(bstrUse);
        }
        pIWSDLClient->GetSOAPOutputBodyEncStyle(bstrMethod, &bstrEncStyle, &bResult);
        if (bResult == VARIANT_TRUE)
        {
            wstrEncStyle = bstrEncStyle;
            SysFreeString(bstrEncStyle);
        }
    }
    SysFreeString(bstrMethod);
    bool bEncoded = false;
    if (wstrUse == L"encoded")
    {
        bEncoded = true;
    }
    if (bEncoded == true)
    {
        // was encoding style defined?
        if (wstrEncStyle.empty() == true)
        {
            // default
            if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
            {
    		    wstrEncStyle = g_pwszSoapEncodingNS;
            }
            else
            {
    		    wstrEncStyle = g_pwszSoap12EncodingNS;
            }
        }
    	pMethod->SetAttribute(wstring(L"SOAP-ENV:encodingStyle"), wstrEncStyle);
    }
	pBody->AddChildElement(pMethod);
    // contents of method wrapper
	WMXMLElement *pChild;
	// generate return element... must be first per SOAP 1.1 spec
	if (Msg.IsRequestPhase() == false && Msg.GetRetValName().empty() == false)
	{
        if (Msg.GetSOAPVersion() == SOAP_VERSION_1_2)
        {
	        // create rpc:result element
            WMXMLElement *pElem = new WMXMLElement(wstring(L"SOAP-RPC:result"));
	        if (pElem == NULL)
	        {
		        return NULL;
	        }
   		    pElem->SetNSPrefix(wstring(L"SOAP-RPC"), wstring(g_pwszSoap12RpcNS));
            pElem->SetContent(Msg.GetRetValName());
    		pMethod->AddChildElement(pElem);
        }
		// defined in WSDL file
        if (bEncoded == true)
        {
		    pChild = SerializeSOAPMsgParam( Msg.GetReturnValue(),
										    Msg.GetMethodName(),
										    Msg.GetRetValName(),
                                            wstrEncStyle,
										    Msg,
										    pIWSDLClient);
        }
        else
        {
		    pChild = SerializeSOAPMsgLitParam(  Msg.GetReturnValue(),
										        Msg.GetMethodName(),
										        Msg.GetRetValName(),
										        Msg,
										        pIWSDLClient);
        }
		if (pChild == NULL)
		{
			delete pEnv;
			return NULL;
		}
		pMethod->AddChildElement(pChild);
	}
	// generate [out] parameters
	bool bIsInParam;
	bool bIsOutParam;
	wstring wstrNamespaceURI;
	wstring wstrName;
	for (bool bResult = Msg.GetFirstMethodParam(wstrNamespaceURI,
											    wstrName,
											    varContent,
											    bIsInParam,
											    bIsOutParam,
											    lPosition);

		 bResult == true;
		 bResult = Msg.GetNextMethodParam(  wstrNamespaceURI,
											wstrName,
											varContent,
											bIsInParam,
											bIsOutParam,
											lPosition)
	)
	{
        if (bEncoded == true)
        {
		    pChild = SerializeSOAPMsgParam( varContent,
										    Msg.GetMethodName(),
										    wstrName,
                                            wstrEncStyle,
										    Msg,
										    pIWSDLClient);
        }
        else
        {
		    pChild = SerializeSOAPMsgLitParam(  varContent,
										        Msg.GetMethodName(),
										        wstrName,
										        Msg,
										        pIWSDLClient);
        }
		if (pChild == NULL)
		{
			delete pEnv;
			return NULL;
		}
		pMethod->AddChildElement(pChild);
		varContent.Clear();
	}
	return pEnv;
}

WMXMLElement *WMSOAPSerializer::SerializeSOAPFaultMsg(WMSOAPMessage& Msg,
													 Iwmwsdlclient2 *pIWSDLClient)
{
	// serialize a locally generated fault message
	WMXMLElement *pEnv = new WMXMLElement(wstring(L"SOAP-ENV:Envelope"));
	if (pEnv == NULL)
	{
        return NULL;
    }
    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
    {
		pEnv->SetNSPrefix(wstring(L"SOAP-ENV"), wstring(g_pwszSoapEnvelopeNS));
    }
    else
    {
		pEnv->SetNSPrefix(wstring(L"SOAP-ENV"), wstring(g_pwszSoap12EnvelopeNS));
    }
	wstring wstrNamespaceURI;
	wstring wstrName;
	variant_t varContent;
	long lPosition;
	bool bResult;
	if (Msg.GetNumFaultHeaderElems() > 0 || Msg.GetNumFaultHeaderElemsLit() > 0)
	{
		WMXMLElement *pHeader = new WMXMLElement(wstring(L"SOAP-ENV:Header"));
		if (pHeader == NULL)
		{
			delete pEnv;
			return NULL;
		}
		pEnv->AddChildElement(pHeader);

		// first get any literal XML headers not deserialized
        WMSOAPHeaderMsgElementLit *pHdrElemLit;
		for (bResult = Msg.GetFirstFaultHeaderElemLit(  &pHdrElemLit,
														lPosition);
			   	bResult == true;
				bResult = Msg.GetNextFaultHeaderElemLit(   &pHdrElemLit,
														lPosition)
		)
		{
            WMXMLElement *pElem = ParseElementLit(pHdrElemLit->m_wstrLiteralXML);
			if (pElem != NULL)
			{
				pHeader->AddChildElement(pElem);
			}
			else
			{
				delete pEnv;
				return NULL;
			}
		}
		// now any deserialized SOAP encoded headers
        WMSOAPHeaderMsgElement *pHdrElem;
		for (bResult = Msg.GetFirstFaultHeaderElem( &pHdrElem,
													lPosition);

			   	bResult == true;
				bResult = Msg.GetNextFaultHeaderElem(  &pHdrElem,
													lPosition)
		)
		{
            WMXMLElement *pElem = SerializeHeaderElem(  pHdrElem->m_wstrNamespaceURI,
                                                        pHdrElem->m_wstrName,
                                                        pHdrElem->m_varContent,
                                                        pHdrElem->m_bMustUnderstand,
                                                        pHdrElem->m_wstrActor,
                                                        pHdrElem->m_bRelay,
														Msg,
														pIWSDLClient);
			if (pElem != NULL)
			{
				pHeader->AddChildElement(pElem);
			}
			else
			{
				delete pEnv;
				return NULL;
			}
			varContent.Clear();
		}
	}
	// now create Body
	WMXMLElement *pBody = new WMXMLElement(wstring(L"SOAP-ENV:Body"));
	if (pBody != NULL)
	{
		pEnv->AddChildElement(pBody);
	}
	else
	{
		delete pEnv;
		return NULL;
	}
	// serialize the fault from the stored data
	WMXMLElement *pFault = new WMXMLElement(wstring(L"SOAP-ENV:Fault"));
	if (pFault != NULL)
	{
		pBody->AddChildElement(pFault);
	}
	else
	{
		delete pEnv;
		return NULL;
	}
    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
    {
	    WMXMLElement *pFaultCode = new WMXMLElement(wstring(L"faultcode"));
	    if (pFaultCode != NULL)
	    {
            wstring wstrContent(L"ns:");
            wstrContent += Msg.GetSOAPFaultCode();
		    pFaultCode->AppendContent(wstrContent);
            pFaultCode->SetAttribute(   wstring(L"xmlns:ns"),
       									Msg.GetSOAPFaultCodeNS());
		    pFault->AddChildElement(pFaultCode);
	    }
	    WMXMLElement *pFaultString = new WMXMLElement(wstring(L"faultstring"));
	    if (pFaultString != NULL)
	    {
		    pFaultString->AppendContent(Msg.GetSOAPFaultString());
		    pFault->AddChildElement(pFaultString);
	    }
	    if (Msg.IsIntermediary() == true || Msg.GetSOAPFaultNode().empty() == false)
	    {
		    WMXMLElement *pFaultActor = new WMXMLElement(wstring(L"faultactor"));
		    if (pFaultActor != NULL)
		    {
			    if (Msg.IsLocalFault() == true)
			    {
				    // if we are an intermediary we MUST include 'faultactor'
				    // if fault was generated by this node
				    pFaultActor->AppendContent(Msg.GetNodeURI());
			    }
			    else
			    {
				    // passing through fault msg received from another actor
				    pFaultActor->AppendContent(Msg.GetSOAPFaultNode());
			    }
			    pFault->AddChildElement(pFaultActor);
		    }
	    }
    }
    else
    {
        WMXMLElement *pFaultCode = new WMXMLElement(wstring(L"SOAP-ENV:Code"));
	    if (pFaultCode != NULL)
	    {
            WMXMLElement *pFaultValue = new WMXMLElement(wstring(L"SOAP-ENV:Value"));
            if (pFaultValue != NULL)
            {
                wstring wstrContent(L"ns:");
                wstrContent += Msg.GetSOAPFaultCode();
		        pFaultValue->AppendContent(wstrContent);
                pFaultValue->SetAttribute(  wstring(L"xmlns:ns"),
    										Msg.GetSOAPFaultCodeNS());
		        pFaultCode->AddChildElement(pFaultValue);
            }
            // is there a subcode?
            if (Msg.GetSOAPFaultSubcode().empty() == false)
            {
                WMXMLElement *pSubcode = new WMXMLElement(wstring(L"SOAP-ENV:Subcode"));
	            if (pSubcode != NULL)
	            {
                    WMXMLElement *pFaultValue = new WMXMLElement(wstring(L"SOAP-ENV:Value"));
                    if (pFaultValue != NULL)
                    {
                        wstring wstrContent(L"ns:");
                        wstrContent += Msg.GetSOAPFaultSubcode();
		                pFaultValue->AppendContent(wstrContent);
                        pFaultValue->SetAttribute(  wstring(L"xmlns:ns"),
    										        Msg.GetSOAPFaultSubcodeNS());
		                pSubcode->AddChildElement(pFaultValue);
                    }
		            pFaultCode->AddChildElement(pSubcode);
	            }
            }
		    pFault->AddChildElement(pFaultCode);
	    }
        WMXMLElement *pReason = new WMXMLElement(wstring(L"SOAP-ENV:Reason"));
	    if (pReason != NULL)
	    {
            if (Msg.GetReasonTextMap().empty() == true)
            {
                WMXMLElement *pText = new WMXMLElement(wstring(L"SOAP-ENV:Text"));
                if (pText != NULL)
                {
                    pText->SetAttribute(wstring(L"xml:lang"), wstring(L"en"));
                    pText->AppendContent(wstring(L"No Reason specified."));
                    pReason->AddChildElement(pText);
                }
            }
            else
            {
                map<wstring, wstring>::iterator iM;
                for (iM = Msg.GetReasonTextMap().begin(); iM != Msg.GetReasonTextMap().end(); iM++)
                {
                    WMXMLElement *pText = new WMXMLElement(wstring(L"SOAP-ENV:Text"));
                    if (pText == NULL)
                    {
                        continue;
                    }
                    pText->SetAttribute(wstring(L"xml:lang"), (*iM).first);
                    pText->AppendContent((*iM).second);
                    pReason->AddChildElement(pText);
                }
            }
		    pFault->AddChildElement(pReason);
	    }
	    if (Msg.IsIntermediary() == true || Msg.GetSOAPFaultNode().empty() == false)
	    {
            WMXMLElement *pFaultNode = new WMXMLElement(wstring(L"SOAP-ENV:Node"));
		    if (pFaultNode != NULL)
		    {
			    if (Msg.IsLocalFault() == true)
			    {
				    // if we are an intermediary we MUST include 'Node'
				    // if fault was generated by this node
				    pFaultNode->AppendContent(Msg.GetNodeURI());
			    }
			    else
			    {
				    // passing through fault msg received from another actor
				    pFaultNode->AppendContent(Msg.GetSOAPFaultNode());
			    }
			    pFault->AddChildElement(pFaultNode);
		    }
	    }
    }
	if (Msg.GetSOAPFaultNeedDetail() == true)
	{
        WMXMLElement *pDetail;
        if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
        {
    		pDetail = new WMXMLElement(wstring(L"detail"));
        }
        else
        {
            pDetail = new WMXMLElement(wstring(L"SOAP-ENV:Detail"));
        }
		if (pDetail != NULL)
		{
			pFault->AddChildElement(pDetail);
		    for (bResult = Msg.GetFirstFaultDetailElem( wstrNamespaceURI,
													    wstrName,
													    varContent,
													    lPosition);

			    bResult == true;
			    bResult = Msg.GetNextFaultDetailElem(  wstrNamespaceURI,
													    wstrName,
													    varContent,
													    lPosition)
		    )
		    {
			    WMXMLElement *pChild = SerializeFaultDetailElem(wstrNamespaceURI,
															    wstrName,
															    varContent,
															    Msg,
															    pIWSDLClient);
			    if (pChild != NULL)
			    {
				    pDetail->AddChildElement(pChild);
			    }
			    varContent.Clear();
		    }
		}
	}
	return pEnv;
}

WMXMLElement *WMSOAPSerializer::SerializeBuiltInType(const wstring& wstrElemName,
													const wstring& wstrType,
													const wstring& wstrTypeNS,
													_variant_t& varValue,
                                                    const wstring& wstrEncStyle,
													WMSOAPMessage& Msg)
{
	// create element
	WMXMLElement *pElem = new WMXMLElement(wstrElemName);
	if (pElem == NULL)
	{
		return NULL;
	}
	// is it nil?
	if (varValue.vt == VT_NULL)
	{
		if (wstrTypeNS == L"http://www.w3.org/1999/XMLSchema")
		{
			pElem->SetAttribute(wstring(L"xsi:null"), wstring(GetXSDInstanceNS(wstrTypeNS)), wstring(L"1"));
		}
		else
		{
			pElem->SetAttribute(wstring(L"xsi:nil"), wstring(GetXSDInstanceNS(wstrTypeNS)), wstring(L"true"));
		}
		return pElem;
	}
	// is it IUnknown (assumed to be DIME attachment)
	if (varValue.vt == VT_UNKNOWN)
	{
		// DIME attachment
		wstring wstrId = GenerateId();
        if (wstrEncStyle.find(g_pwszSoapEncodingNS) == 0)
        {
            // use SOAP 1.1 encoding
		    pElem->SetAttribute(wstring(L"href"), wstrId);
        }
        else
        {
            // SOAP 1.2 encoding
            pElem->SetAttribute(wstring(L"SOAP-ENC:ref"), wstring(g_pwszSoap12EncodingNS), wstrId);
        }
		// add to attachments
		bool bRtn;
		if (Msg.IsRequestPhase() == true)
		{
			bRtn = Msg.AddDIMEAttachmentReq(wstrId,
											wstring(L""),
											DIME_TYPENAME_FORMAT_UNKNOWN,
											varValue.punkVal);
		}
		else
		{
			bRtn = Msg.AddDIMEAttachmentResp(   wstrId,
												wstring(L""),
												DIME_TYPENAME_FORMAT_UNKNOWN,
												varValue.punkVal);
		}
		if (bRtn == false)
		{
			delete pElem;
			return NULL;
		}
		return pElem;
	}
	// look it up in VARTYPE map
	wstring wstrExplicitType;
	VARTYPE Vartype;
	map<wstring, VARTYPE>::iterator iV;
	if ((iV = m_mapVarTypes.find(wstrType)) != m_mapVarTypes.end())
	{
		// simple type - namespace xsd or SOAP-ENC
		Vartype = (*iV).second;
		// is this a polymorphic type?
		if (Vartype == XT_ANYTYPE)
		{
			// yes... evaluate VARTYPE
			switch (varValue.vt)
			{
			case VT_I1:
				wstrExplicitType = L"byte";
				Vartype = VT_I1;
				break;
			case VT_UI1:
				wstrExplicitType = L"unsignedByte";
				Vartype = VT_UI1;
				break;
			case VT_I2:
				wstrExplicitType = L"short";
				Vartype = VT_I2;
				break;
			case VT_UI2:
				wstrExplicitType = L"unsignedShort";
				Vartype = VT_UI2;
				break;
			case VT_I4:
				wstrExplicitType = L"int";
				Vartype = VT_I4;
				break;
			case VT_UI4:
				wstrExplicitType = L"unsignedInt";
				Vartype = VT_UI4;
				break;
			case VT_R4:
				wstrExplicitType = L"float";
				Vartype = VT_R4;
				break;
			case VT_R8:
				wstrExplicitType = L"double";
				Vartype = VT_R8;
				break;
			case VT_BSTR:
				wstrExplicitType = L"string";
				Vartype = VT_BSTR;
				break;
			case VT_DECIMAL:
				wstrExplicitType = L"decimal";
				Vartype = VT_DECIMAL;
				break;
			case VT_DATE:
				if (wstrTypeNS == g_pwszXMLSchemaNS1999)
				{
					wstrExplicitType = L"timeInstant";
				}
				else
				{
					wstrExplicitType = L"dateTime";
				}
				Vartype = XT_DATETIME;
				break;
			case VT_BOOL:
				wstrExplicitType = L"boolean";
				Vartype = XT_BOOLEAN;
				break;
			default:
				{
					// not supported
					delete pElem;
					return NULL;
				}
			}
			if (Msg.ExplicitTypingEnabled() == false)
			{
				// override so that xsi:type attribute is given
				pElem->SetNSPrefix(wstring(L"ns"), wstrTypeNS);
				wstring wstrTemp = L"ns:";
				wstrTemp += wstrExplicitType;
				pElem->SetAttribute(wstring(L"xsi:type"), wstring(GetXSDInstanceNS(wstrTypeNS)), wstrTemp);
			}
		}
		else
		{
			wstrExplicitType = wstrType;
		}
		// does it need special handling?
		if (Vartype < XT_ANYTYPE)
		{
			// no... convert directly
			varValue.ChangeType(VT_BSTR);
			pElem->AppendContent(wstring(varValue.bstrVal));
		}
		else
		{
			// yes... these types don't map directly to automation types
			switch(Vartype)
			{
			case XT_ANYTYPE:
			case XT_ANYURI:
				{
					// treat as a string
					pElem->AppendContent(wstring(varValue.bstrVal));
				}
				break;
			case XT_BOOLEAN:
				{
					// use "0" or "1"
					if (varValue.boolVal == VARIANT_FALSE)
					{
						pElem->AppendContent(wstring(L"0"));
					}
					else
					{
						pElem->AppendContent(wstring(L"1"));
					}
				}
				break;
			case XT_BASE64:
				{
					// xsd:base64Binary or SOAP-ENC:base64
					SAFEARRAY *pArray = varValue.parray;
					SerializeBase64Vector(pArray, pElem);
				}
				break;
			case XT_HEXBIN:
				{
					// xsd:hexBinary
					SAFEARRAY *pArray = varValue.parray;
					SerializeHexBinaryVector(pArray, pElem);
				}
				break;
			case XT_DATETIME:
				{
					// xsd:dateTime
					DATE d = varValue.date;
					SerializeDateTime(d, pElem);
				}
				break;
			case XT_DATE:
				{
					// xsd:date
					DATE d = varValue.date;
					SerializeDate(d, pElem);
				}
				break;
			case XT_TIME:
				{
					// xsd:time
					DATE d = varValue.date;
					SerializeTime(d, pElem);
				}
				break;
			default:
				{
					// should never happen
					delete pElem;
					return NULL;
				}
			}
		}
	}
	else
	{
		// should never happen
		delete pElem;
		return NULL;
	}
	if (Msg.ExplicitTypingEnabled() == true)
	{
		pElem->SetNSPrefix(wstring(L"ns"), wstrTypeNS);
		wstring wstrTemp = L"ns:";
		wstrTemp += wstrExplicitType;
		pElem->SetAttribute(wstring(L"xsi:type"), wstring(GetXSDInstanceNS(wstrTypeNS)), wstrTemp);
	}
    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_2 && Msg.NodeTypeEnabled() == true)
    {
        pElem->SetAttribute(wstring(L"SOAP-ENC:nodeType"), wstring(g_pwszSoap12EncodingNS), wstring(L"simple"));
    }
	return pElem;
}

WMXMLElement *WMSOAPSerializer::SerializeBuiltInTypeLit(const wstring& wstrElemName,
                                                        const wstring& wstrElemNS,
													    const wstring& wstrType,
													    const wstring& wstrTypeNS,
													    _variant_t& varValue,
													    WMSOAPMessage& Msg)
{
    // create element
    WMXMLElement *pElem;
    if (wstrElemNS.empty() == true)
    {
	    pElem = new WMXMLElement(wstrElemName);
    }
    else
    {
        wstring wstrTemp(L"nsb:");
        wstrTemp += wstrElemName;
	    pElem = new WMXMLElement(wstrTemp);
        if (pElem != NULL)
        {
            pElem->SetNSPrefix(wstring(L"nsb"), wstrElemNS);
        }
    }
	if (pElem == NULL)
	{
		return NULL;
	}
	// is it nil?
	if (varValue.vt == VT_NULL)
	{
		if (wstrTypeNS == L"http://www.w3.org/1999/XMLSchema")
		{
			pElem->SetAttribute(wstring(L"xsi:null"), wstring(GetXSDInstanceNS(wstrTypeNS)), wstring(L"1"));
		}
		else
		{
			pElem->SetAttribute(wstring(L"xsi:nil"), wstring(GetXSDInstanceNS(wstrTypeNS)), wstring(L"true"));
		}
		return pElem;
	}
	// look it up in VARTYPE map
	wstring wstrExplicitType;
	VARTYPE Vartype;
	map<wstring, VARTYPE>::iterator iV;
	if ((iV = m_mapVarTypes.find(wstrType)) != m_mapVarTypes.end())
	{
		// simple type - namespace xsd or SOAP-ENC
		Vartype = (*iV).second;
		// is this a polymorphic type?
		if (Vartype == XT_ANYTYPE)
		{
			// yes... evaluate VARTYPE
			switch (varValue.vt)
			{
			case VT_I1:
				wstrExplicitType = L"byte";
				Vartype = VT_I1;
				break;
			case VT_UI1:
				wstrExplicitType = L"unsignedByte";
				Vartype = VT_UI1;
				break;
			case VT_I2:
				wstrExplicitType = L"short";
				Vartype = VT_I2;
				break;
			case VT_UI2:
				wstrExplicitType = L"unsignedShort";
				Vartype = VT_UI2;
				break;
			case VT_I4:
				wstrExplicitType = L"int";
				Vartype = VT_I4;
				break;
			case VT_UI4:
				wstrExplicitType = L"unsignedInt";
				Vartype = VT_UI4;
				break;
			case VT_R4:
				wstrExplicitType = L"float";
				Vartype = VT_R4;
				break;
			case VT_R8:
				wstrExplicitType = L"double";
				Vartype = VT_R8;
				break;
			case VT_BSTR:
				wstrExplicitType = L"string";
				Vartype = VT_BSTR;
				break;
			case VT_DECIMAL:
				wstrExplicitType = L"decimal";
				Vartype = VT_DECIMAL;
				break;
			case VT_DATE:
				if (wstrTypeNS == g_pwszXMLSchemaNS1999)
				{
					wstrExplicitType = L"timeInstant";
				}
				else
				{
					wstrExplicitType = L"dateTime";
				}
				Vartype = XT_DATETIME;
				break;
			case VT_BOOL:
				wstrExplicitType = L"boolean";
				Vartype = XT_BOOLEAN;
				break;
			default:
				{
					// not supported
					delete pElem;
					return NULL;
				}
			}
			if (Msg.ExplicitTypingEnabled() == false)
			{
				// override so that xsi:type attribute is given
				pElem->SetNSPrefix(wstring(L"ns"), wstrTypeNS);
				wstring wstrTemp = L"ns:";
				wstrTemp += wstrExplicitType;
				pElem->SetAttribute(wstring(L"xsi:type"), wstring(GetXSDInstanceNS(wstrTypeNS)), wstrTemp);
			}
		}
		else
		{
			wstrExplicitType = wstrType;
		}
		// does it need special handling?
		if (Vartype < XT_ANYTYPE)
		{
			// no... convert directly
			varValue.ChangeType(VT_BSTR);
			pElem->AppendContent(wstring(varValue.bstrVal));
		}
		else
		{
			// yes... these types don't map directly to automation types
			switch(Vartype)
			{
			case XT_ANYTYPE:
			case XT_ANYURI:
				{
					// treat as a string
					pElem->AppendContent(wstring(varValue.bstrVal));
				}
				break;
			case XT_BOOLEAN:
				{
					// use "0" or "1"
					if (varValue.boolVal == VARIANT_FALSE)
					{
						pElem->AppendContent(wstring(L"0"));
					}
					else
					{
						pElem->AppendContent(wstring(L"1"));
					}
				}
				break;
			case XT_BASE64:
				{
					// xsd:base64Binary or SOAP-ENC:base64
					SAFEARRAY *pArray = varValue.parray;
					SerializeBase64Vector(pArray, pElem);
				}
				break;
			case XT_HEXBIN:
				{
					// xsd:hexBinary
					SAFEARRAY *pArray = varValue.parray;
					SerializeHexBinaryVector(pArray, pElem);
				}
				break;
			case XT_DATETIME:
				{
					// xsd:dateTime
					DATE d = varValue.date;
					SerializeDateTime(d, pElem);
				}
				break;
			case XT_DATE:
				{
					// xsd:date
					DATE d = varValue.date;
					SerializeDate(d, pElem);
				}
				break;
			case XT_TIME:
				{
					// xsd:time
					DATE d = varValue.date;
					SerializeTime(d, pElem);
				}
				break;
			default:
				{
					// should never happen
					delete pElem;
					return NULL;
				}
			}
		}
	}
	else
	{
		// should never happen
		delete pElem;
		return NULL;
	}
	if (Msg.ExplicitTypingEnabled() == true)
	{
		pElem->SetNSPrefix(wstring(L"ns"), wstrTypeNS);
		wstring wstrTemp = L"ns:";
		wstrTemp += wstrExplicitType;
		pElem->SetAttribute(wstring(L"xsi:type"), wstring(GetXSDInstanceNS(wstrTypeNS)), wstrTemp);
	}
	return pElem;
}

WMXMLElement *WMSOAPSerializer::SerializeComplexType(const wstring& wstrElemName,
													BSTR bstrType,
													BSTR bstrTypeNS,
													_variant_t& varValue,
                                                    const wstring& wstrEncStyle,
													WMSOAPMessage& Msg,
													Iwmwsdlclient2 *pIWSDLClient)
{
	WMXMLElement *pElem = new WMXMLElement(wstrElemName);
	if (pElem == NULL)
	{
		return NULL;
	}
	BSTR bstrBaseType, bstrBaseTypeNS;
	VARIANT_BOOL bResult;
	pIWSDLClient->GetComplexBaseType(	bstrType,
										bstrTypeNS,
										&bstrBaseType,
										&bstrBaseTypeNS,
										&bResult);
	if (bResult == VARIANT_FALSE)
	{
		delete pElem;
		return NULL;
	}
	wstring wstrBaseType(bstrBaseType);
	wstring wstrBaseTypeNS(bstrBaseTypeNS);
	SysFreeString(bstrBaseType);
	SysFreeString(bstrBaseTypeNS);
	if (IsSoapEncType(wstrBaseTypeNS))
	{
		if (wstrBaseType == L"Array")
		{
			// get SAFEARRAY pointer
			SAFEARRAY *pArray = varValue.parray;
			if (pArray == NULL)
			{
				delete pElem;
				return NULL;
			}
			// look up data type in WSDL
			BSTR bstrArrayType, bstrArrayTypeNS;
			pIWSDLClient->GetArrayType( bstrType,
										bstrTypeNS,
										&bstrArrayType,
										&bstrArrayTypeNS,
										&bResult);
			if (bResult == VARIANT_FALSE)
			{
				// not properly defined in WSDL file
				delete pElem;
				return NULL;
			}
			// extract values from array
			long lNumDims = SafeArrayGetDim(pArray);
			long *arrIndex = new long[lNumDims];
			SerializeSafeArray( 0,
								arrIndex,
								pArray,
								pElem,
								bstrArrayType,
								bstrArrayTypeNS,
                                wstrEncStyle,
								Msg,
								pIWSDLClient);
			delete [] arrIndex;
			wstring wstrArrayType(bstrArrayType);
			wstring wstrArrayTypeNS(bstrArrayTypeNS);
			SysFreeString(bstrArrayType);
			SysFreeString(bstrArrayTypeNS);
			// output array attributes
            if (wstrEncStyle.find(g_pwszSoapEncodingNS) == 0)
            {
    			// output arrayType attribute
			    wstring wstrTemp = L"nsa:";
			    wstrArrayType = wstrTemp + wstrArrayType;
			    wstrTemp = L"[";
			    for (int k = 0; k < lNumDims; k++)
			    {
				    wchar_t wszBuf[16];
				    if (k > 0)
				    {
					    _snwprintf(wszBuf, 15, L",%d", pArray->rgsabound[k].cElements + pArray->rgsabound[k].lLbound);
				    }
				    else
				    {
					    _snwprintf(wszBuf, 15, L"%d", pArray->rgsabound[k].cElements + pArray->rgsabound[k].lLbound);
				    }
                    wszBuf[15] = 0;
				    wstrTemp += wszBuf;
			    }
			    wstrTemp += L"]";
			    pElem->SetAttribute(wstring(L"SOAP-ENC:arrayType"),
								    wstring(g_pwszSoapEncodingNS),
								    wstrArrayType + wstrTemp);
			    // output offset attribute
			    wstrTemp = L"[";
			    for (k = 0; k < lNumDims; k++)
			    {
				    wchar_t wszBuf[16];
				    if (k > 0)
				    {
					    _snwprintf(wszBuf, 15, L",%d", pArray->rgsabound[k].lLbound);
				    }
				    else
				    {
					    _snwprintf(wszBuf, 15, L"%d", pArray->rgsabound[k].lLbound);
				    }
                    wszBuf[15] = 0;
				    wstrTemp += wszBuf;
			    }
			    wstrTemp += L"]";
			    pElem->SetAttribute(wstring(L"SOAP-ENC:offset"),
								    wstring(g_pwszSoapEncodingNS),
								    wstrTemp);
            }
            else // SOAP 1.2 encoding
            {
    			// output itemType attribute
			    wstring wstrTemp = L"nsa:";
			    wstrArrayType = wstrTemp + wstrArrayType;
			    pElem->SetAttribute(wstring(L"SOAP-ENC:itemType"),
								    wstring(g_pwszSoap12EncodingNS),
								    wstrArrayType);
			    // output arraySize attribute
			    wstrTemp = L"";
			    for (int k = 0; k < lNumDims; k++)
			    {
				    wchar_t wszBuf[16];
				    if (k > 0)
				    {
					    _snwprintf(wszBuf, 15, L" %d", pArray->rgsabound[k].cElements + pArray->rgsabound[k].lLbound);
				    }
				    else
				    {
					    _snwprintf(wszBuf, 15, L"%d", pArray->rgsabound[k].cElements + pArray->rgsabound[k].lLbound);
				    }
                    wszBuf[15] = 0;
				    wstrTemp += wszBuf;
			    }
			    pElem->SetAttribute(wstring(L"SOAP-ENC:arraySize"),
								    wstring(g_pwszSoap12EncodingNS),
								    wstrTemp);
            }
			pElem->SetNSPrefix(wstring(L"nsa"), wstrArrayTypeNS);
			if (Msg.ExplicitTypingEnabled() == true)
			{
				pElem->SetAttribute(wstring(L"xsi:type"), wstring(g_pwszXMLSchemaInstNS2001), wstring(L"SOAP-ENC:Array"));
			}
            if (Msg.GetSOAPVersion() == SOAP_VERSION_1_2 && Msg.NodeTypeEnabled() == true)
            {
                pElem->SetAttribute(wstring(L"SOAP-ENC:nodeType"), wstring(g_pwszSoap12EncodingNS), wstring(L"array"));
            }
		}
		else if (wstrBaseType == L"Struct")
		{
			// SOAP-ENC:Struct
			IDispatch *pIDispatch = varValue.pdispVal;
			if (SerializeStruct(bstrType,
								bstrTypeNS,
								pIDispatch,
								pElem,
                                wstrEncStyle,
								Msg,
								pIWSDLClient) == false)
			{
				// trouble
				delete pElem;
				return NULL;
			}
		}
		else
		{
			// may be based on SOAP-ENC built-in type
			delete pElem;
			return SerializeBuiltInType(wstrElemName,
										wstrBaseType,
										wstrBaseTypeNS,
										varValue,
                                        wstrEncStyle,
										Msg);
		}
	}
	else if (IsXSDType(wstrBaseTypeNS))
	{
		// may be based on built-in type
		delete pElem;
		return SerializeBuiltInType(wstrElemName,
									wstrBaseType,
									wstrBaseTypeNS,
									varValue,
                                    wstrEncStyle,
									Msg);
	}
	else
	{
		// assume complexType
		IDispatch *pIDispatch = varValue.pdispVal;
		if (SerializeStruct(bstrType,
							bstrTypeNS,
							pIDispatch,
							pElem,
                            wstrEncStyle,
							Msg,
							pIWSDLClient) == false)
		{
			// trouble
			delete pElem;
			return NULL;
		}
	}
	return pElem;
}

WMXMLElement *WMSOAPSerializer::SerializeComplexTypeLit(const wstring& wstrElemName,
                                                        const wstring& wstrElemNS,
													    BSTR bstrType,
													    BSTR bstrTypeNS,
													    _variant_t& varValue,
													    WMSOAPMessage& Msg,
													    Iwmwsdlclient2 *pIWSDLClient)
{
    WMXMLElement *pElem = NULL;
	// see if it is array type
	BSTR bstrItemType, bstrItemTypeNS, bstrItemName, bstrItemNS;
    VARIANT_BOOL bResult;
	pIWSDLClient->GetArrayTypeLit(	bstrType,
								    bstrTypeNS,
								    &bstrItemType,
								    &bstrItemTypeNS,
                                    &bstrItemName,
                                    &bstrItemNS,
								    &bResult);
	if (bResult == VARIANT_TRUE)
	{
	    wstring wstrItemType(bstrItemType);
	    wstring wstrItemTypeNS(bstrItemTypeNS);
	    wstring wstrItemName(bstrItemName);
	    wstring wstrItemNS(bstrItemNS);
	    SysFreeString(bstrItemType);
	    SysFreeString(bstrItemTypeNS);
	    SysFreeString(bstrItemName);
	    SysFreeString(bstrItemNS);
        // create element
        if (wstrElemNS.empty() == true)
        {
	        pElem = new WMXMLElement(wstrElemName);
        }
        else
        {
            wstring wstrTemp(L"nsa:");
            wstrTemp += wstrElemName;
	        pElem = new WMXMLElement(wstrTemp);
            if (pElem != NULL)
            {
                pElem->SetNSPrefix(wstring(L"nsa"), wstrElemNS);
            }
        }
	    if (pElem == NULL)
	    {
		    return NULL;
	    }
		// get SAFEARRAY pointer
		SAFEARRAY *pArray = varValue.parray;
		if (pArray == NULL)
		{
			delete pElem;
			return NULL;
		}
		// extract values from array
		SerializeSafeArrayLit(  pArray,
							    pElem,
							    wstrItemType,
							    wstrItemTypeNS,
                                wstrItemName,
                                wstrItemNS,
							    Msg,
							    pIWSDLClient);
		if (Msg.ExplicitTypingEnabled() == true)
		{
		    pElem->SetNSPrefix(wstring(L"ns"), wstring(bstrTypeNS));
		    wstring wstrTemp = L"ns:";
		    wstrTemp += bstrType;
		    pElem->SetAttribute(wstring(L"xsi:type"), wstring(g_pwszXMLSchemaInstNS2001), wstrTemp);
		}
	}
	else
	{
        // must be complex type, possibly derived from simple type
	    BSTR bstrBaseType, bstrBaseTypeNS;
	    VARIANT_BOOL bResult;
	    pIWSDLClient->GetComplexBaseType(	bstrType,
										    bstrTypeNS,
										    &bstrBaseType,
										    &bstrBaseTypeNS,
										    &bResult);
	    if (bResult == VARIANT_FALSE)
	    {
		    return NULL;
	    }
	    wstring wstrBaseType(bstrBaseType);
	    wstring wstrBaseTypeNS(bstrBaseTypeNS);
	    SysFreeString(bstrBaseType);
	    SysFreeString(bstrBaseTypeNS);
        if (IsXSDType(wstrBaseTypeNS) || IsSoapEncType(wstrBaseTypeNS))
	    {
		    // may be based on built-in type
		    return SerializeBuiltInTypeLit( wstrElemName,
                                            wstrElemNS,
									        wstrBaseType,
									        wstrBaseTypeNS,
									        varValue,
									        Msg);
	    }
	    else
	    {
            // create element
            if (wstrElemNS.empty() == true)
            {
	            pElem = new WMXMLElement(wstrElemName);
            }
            else
            {
                wstring wstrTemp(L"nss:");
                wstrTemp += wstrElemName;
	            pElem = new WMXMLElement(wstrTemp);
                if (pElem != NULL)
                {
                    pElem->SetNSPrefix(wstring(L"nss"), wstrElemNS);
                }
            }
	        if (pElem == NULL)
	        {
		        return NULL;
	        }
		    // assume complexType
		    IDispatch *pIDispatch = varValue.pdispVal;
		    if (SerializeStructLit( bstrType,
							        bstrTypeNS,
							        pIDispatch,
							        pElem,
							        Msg,
							        pIWSDLClient) == false)
		    {
			    // trouble
			    delete pElem;
			    return NULL;
		    }
	    }
    }
	return pElem;
}

WMXMLElement *WMSOAPSerializer::SerializeSOAPMsgParam(_variant_t& varData,
													  const wstring& wstrMethodName,
													  const wstring& wstrElemName,
                                                      const wstring& wstrEncStyle,
													  WMSOAPMessage& Msg,
													  Iwmwsdlclient2 *pIWSDLClient)
{
    BSTR bstrMethod = SysAllocString(Msg.GetMethodName().c_str());
	// get data type of part
	VARIANT_BOOL bResult;
	BSTR bstrParamName, bstrPartType, bstrPartTypeNS;
	bstrParamName = SysAllocString(wstrElemName.c_str());
	if (Msg.IsRequestPhase() == false)
	{
		pIWSDLClient->GetOutputMsgPartType( bstrMethod,
											bstrParamName,
											&bstrPartType,
											&bstrPartTypeNS,
											&bResult);
	}
	else
	{
		pIWSDLClient->GetInputMsgPartType(  bstrMethod,
											bstrParamName,
											&bstrPartType,
											&bstrPartTypeNS,
											&bResult);
	}
	if (bResult == VARIANT_FALSE)
	{
		// not defined properly in WSDL file
		SysFreeString(bstrMethod);
		SysFreeString(bstrParamName);
		return NULL;
	}
	wstring wstrPartType(bstrPartType);
	wstring wstrPartTypeNS(bstrPartTypeNS);
	WMXMLElement *pParam = NULL;
	// is this a built-in type?
	if (IsXSDType(wstrPartTypeNS) || IsSoapEncType(wstrPartTypeNS))
	{
		// built-in type
		pParam = SerializeBuiltInType(  wstrElemName,
										wstrPartType,
										wstrPartTypeNS,
										varData,
                                        wstrEncStyle,
										Msg);
	}
	else
	{
		// complex type
		pParam = SerializeComplexType(  wstrElemName,
										bstrPartType,
										bstrPartTypeNS,
										varData,
                                        wstrEncStyle,
										Msg,
										pIWSDLClient);
	}
	SysFreeString(bstrMethod);
	SysFreeString(bstrParamName);
	SysFreeString(bstrPartType);
	SysFreeString(bstrPartTypeNS);
	return pParam;
}

WMXMLElement *WMSOAPSerializer::SerializeSOAPMsgLitParam(   _variant_t& varData,
													        const wstring& wstrMethodName,
													        const wstring& wstrElemName,
													        WMSOAPMessage& Msg,
													        Iwmwsdlclient2 *pIWSDLClient)
{
    BSTR bstrMethod = SysAllocString(Msg.GetMethodName().c_str());
	// get data type of part
	VARIANT_BOOL bResult;
	BSTR bstrParamName, bstrPartType, bstrPartTypeNS;
	bstrParamName = SysAllocString(wstrElemName.c_str());
	if (Msg.IsRequestPhase() == false)
	{
		pIWSDLClient->GetOutputMsgPartType( bstrMethod,
											bstrParamName,
											&bstrPartType,
											&bstrPartTypeNS,
											&bResult);
	}
	else
	{
		pIWSDLClient->GetInputMsgPartType(  bstrMethod,
											bstrParamName,
											&bstrPartType,
											&bstrPartTypeNS,
											&bResult);
	}
	if (bResult == VARIANT_FALSE)
	{
		// not defined properly in WSDL file
		SysFreeString(bstrMethod);
		SysFreeString(bstrParamName);
		return NULL;
	}
	SysFreeString(bstrMethod);
	SysFreeString(bstrParamName);
	wstring wstrPartType(bstrPartType);
	wstring wstrPartTypeNS(bstrPartTypeNS);
	WMXMLElement *pParam = NULL;
	// is this a built-in type?
	if (IsXSDType(wstrPartTypeNS) || IsSoapEncType(wstrPartTypeNS))
	{
		// built-in type
		pParam = SerializeBuiltInTypeLit(   wstrElemName,
                                            wstring(L""),
										    wstrPartType,
										    wstrPartTypeNS,
										    varData,
										    Msg);
	}
	else
	{
		// complex type
		pParam = SerializeComplexTypeLit(   wstrElemName,
                                            wstring(L""),
										    bstrPartType,
										    bstrPartTypeNS,
										    varData,
										    Msg,
										    pIWSDLClient);
	}
	SysFreeString(bstrPartType);
	SysFreeString(bstrPartTypeNS);
	return pParam;
}

WMXMLElement *WMSOAPSerializer::SerializeFaultDetailElem(   const wstring& wstrNS,
														    const wstring& wstrName,
														    _variant_t& varValue,
														    WMSOAPMessage& Msg,
														    Iwmwsdlclient2 *pIWSDLClient)
{
	BSTR bstrName, bstrNS, bstrType, bstrTypeNS;
	bstrName = SysAllocString(wstrName.c_str());
	bstrNS = SysAllocString(wstrNS.c_str());
	// get operation name
	BSTR bstrOpName = SysAllocString(Msg.GetMethodName().c_str());
    // is this encoded or literal?
    wstring wstrUse;
    BSTR bstrUse;
	VARIANT_BOOL bResult;
	pIWSDLClient->GetFaultDetailUse(bstrOpName,
									bstrName,
									bstrNS,
									&bstrUse,
									&bResult);
	if (bResult == VARIANT_TRUE)
	{
        wstrUse = bstrUse;
        SysFreeString(bstrUse);
    }
	// see if this is declared in soap:fault ext element
	pIWSDLClient->GetFaultDetailPartType(   bstrOpName,
										    bstrName,
										    bstrNS,
										    &bstrType,
										    &bstrTypeNS,
										    &bResult);
	if (bResult == VARIANT_FALSE)
	{
		// not properly defined in WSDL file
	    SysFreeString(bstrOpName);
	    SysFreeString(bstrName);
	    SysFreeString(bstrNS);
		return NULL;
	}
	wstring wstrType(bstrType);
	wstring wstrTypeNS(bstrTypeNS);
	WMXMLElement *pElem = NULL;
    if (wstrUse == L"encoded")
    {
        // get encodingStyle specified in WSDL
        BSTR bstrEncStyle = NULL;
        wstring wstrEncStyle;
        pIWSDLClient->GetFaultDetailEncStyle(   bstrOpName,
                                                bstrName,
                                                bstrNS,
                                                &bstrEncStyle,
                                                &bResult);
        if (bResult == VARIANT_TRUE)
        {
            wstrEncStyle = bstrEncStyle;
            SysFreeString(bstrEncStyle);
        }
        else
	    {
		    // not properly defined in WSDL file
	        SysFreeString(bstrOpName);
	        SysFreeString(bstrType);
	        SysFreeString(bstrTypeNS);
		    return NULL;
	    }
	    // is this a built-in type?
	    if (IsXSDType(wstrTypeNS) || IsSoapEncType(wstrTypeNS))
	    {
		    // built-in type
		    pElem = SerializeBuiltInType(wstring(L"d:") + wstrName,
									    wstrType,
									    wstrTypeNS,
									    varValue,
                                        wstrEncStyle,
									    Msg);
	    }
	    else
	    {
		    // complex type
		    pElem = SerializeComplexType(wstring(L"d:") + wstrName,
									    bstrType,
									    bstrTypeNS,
									    varValue,
                                        wstrEncStyle,
									    Msg,
									    pIWSDLClient);
	    }
	    // attribute
	    if (pElem != NULL)
	    {
		    pElem->SetNSPrefix(wstring(L"d"), wstrNS);
		    pElem->SetAttribute(wstring(L"SOAP-ENV:encodingStyle"), wstrEncStyle);
	    }
    }
    else
    {
	    // is this a built-in type?
	    if (IsXSDType(wstrTypeNS) || IsSoapEncType(wstrTypeNS))
	    {
		    // built-in type
		    pElem = SerializeBuiltInTypeLit(wstrName,
                                            wstrNS,
									        wstrType,
									        wstrTypeNS,
									        varValue,
									        Msg);
	    }
	    else
	    {
		    // complex type
		    pElem = SerializeComplexTypeLit(wstrName,
                                            wstrNS,
									        bstrType,
									        bstrTypeNS,
									        varValue,
									        Msg,
									        pIWSDLClient);
	    }
    }
	SysFreeString(bstrType);
	SysFreeString(bstrTypeNS);
	SysFreeString(bstrName);
	SysFreeString(bstrNS);
	SysFreeString(bstrOpName);
	return pElem;
}

WMXMLElement *WMSOAPSerializer::SerializeHeaderElem(const wstring& wstrNS,
													const wstring& wstrName,
													_variant_t& varValue,
													bool bMustUnderstand,
													const wstring& wstrActor,
                                                    bool bRelay,
													WMSOAPMessage& Msg,
													Iwmwsdlclient2 *pIWSDLClient)
{
	BSTR bstrName, bstrNS, bstrType, bstrTypeNS;
	bstrName = SysAllocString(wstrName.c_str());
	bstrNS = SysAllocString(wstrNS.c_str());
	VARIANT_BOOL bResult;
	// get operation name
	BSTR bstrOpName = SysAllocString(Msg.GetMethodName().c_str());
	// see if this is declared in soap:header ext element
	if (Msg.IsRequestPhase() == false)
	{
		if (Msg.IsSOAPFault() == true)
		{
			// fault header sent in response msg declared within input soap:header in WSDL
			pIWSDLClient->GetInHeaderFaultPartType( bstrOpName,
													bstrName,
													bstrNS,
													&bstrType,
													&bstrTypeNS,
													&bResult);
		}
		else
		{
			pIWSDLClient->GetOutputHeaderPartType(  bstrOpName,
													bstrName,
													bstrNS,
													&bstrType,
													&bstrTypeNS,
													&bResult);
		}
	}
	else
	{
		pIWSDLClient->GetInputHeaderPartType(bstrOpName,
											bstrName,
											bstrNS,
											&bstrType,
											&bstrTypeNS,
											&bResult);
	}
	if (bResult == VARIANT_FALSE)
	{
		// see if is declared in a schema
		pIWSDLClient->GetHeaderElemType(bstrName,
										bstrNS,
										&bstrType,
										&bstrTypeNS,
										&bResult);
	}
	if (bResult == VARIANT_FALSE)
	{
		// not properly defined in WSDL file
	    SysFreeString(bstrOpName);
	    SysFreeString(bstrName);
	    SysFreeString(bstrNS);
		return NULL;
	}
    // get encoding style
    BSTR bstrEncStyle;
    wstring wstrEncStyle;
	if (Msg.IsRequestPhase() == false)
	{
		if (Msg.IsSOAPFault() == true)
		{
			// fault header sent in response msg declared within input soap:header in WSDL
			pIWSDLClient->GetInHeaderFaultPartEncStyle( bstrOpName,
													    bstrName,
													    bstrNS,
													    &bstrEncStyle,
													    &bResult);
		}
		else
		{
			pIWSDLClient->GetOutputHeaderPartEncStyle(  bstrOpName,
													    bstrName,
													    bstrNS,
													    &bstrEncStyle,
													    &bResult);
		}
	}
	else
	{
		pIWSDLClient->GetInputHeaderPartEncStyle(bstrOpName,
											    bstrName,
											    bstrNS,
											    &bstrEncStyle,
											    &bResult);
	}
	if (bResult == VARIANT_FALSE)
	{
        // default
        if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
        {
		    wstrEncStyle = g_pwszSoapEncodingNS;
        }
        else
        {
		    wstrEncStyle = g_pwszSoap12EncodingNS;
        }
	}
    else
    {
        wstrEncStyle = bstrEncStyle;
        SysFreeString(bstrEncStyle);
    }
	SysFreeString(bstrOpName);
	SysFreeString(bstrName);
	SysFreeString(bstrNS);
	wstring wstrType(bstrType);
	wstring wstrTypeNS(bstrTypeNS);
	WMXMLElement *pEntry = NULL;
	// is this a built-in type?
	if (IsXSDType(wstrTypeNS) || IsSoapEncType(wstrTypeNS))
	{
		// built-in type
		pEntry = SerializeBuiltInType(  wstring(L"h:") + wstrName,
										wstrType,
										wstrTypeNS,
										varValue,
                                        wstrEncStyle,
										Msg);
	}
	else
	{
		// complex type
		pEntry = SerializeComplexType(  wstring(L"h:") + wstrName,
										bstrType,
										bstrTypeNS,
										varValue,
                                        wstrEncStyle,
										Msg,
										pIWSDLClient);
	}
	SysFreeString(bstrType);
	SysFreeString(bstrTypeNS);
	// attributes
	if (pEntry != NULL)
	{
		pEntry->SetNSPrefix(wstring(L"h"), wstrNS);
		pEntry->SetAttribute(wstring(L"SOAP-ENV:encodingStyle"), wstrEncStyle);
		if (wstrActor.empty() == false)
		{
            if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
            {
			    pEntry->SetAttribute(wstring(L"SOAP-ENV:actor"), wstrActor);
            }
            else
            {
			    pEntry->SetAttribute(wstring(L"SOAP-ENV:role"), wstrActor);
            }
		}
		if (bMustUnderstand == true)
		{
            if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
            {
			    pEntry->SetAttribute(wstring(L"SOAP-ENV:mustUnderstand"), wstring(L"1"));
            }
            else
            {
			    pEntry->SetAttribute(wstring(L"SOAP-ENV:mustUnderstand"), wstring(L"true"));
            }
		}
		if (bRelay == true && Msg.GetSOAPVersion() == SOAP_VERSION_1_2)
		{
			pEntry->SetAttribute(wstring(L"SOAP-ENV:relay"), wstring(L"true"));
		}
	}
	return pEntry;
}

WMXMLElement *WMSOAPSerializer::ParseElementLit(const wstring& wstrLiteralXML)
{
	// set up for parsing
	WMXMLParser Parser;
	bool bRtn = true;
	// parse the XML fragment representing the header entry
	try
	{
		Parser.ParseXML_WideString(wstrLiteralXML, NULL);
	}
	catch(int e)
	{
		int x = e;
		return NULL;
	}
	// return the WMXMLElement pointer representing root node of parse tree
	return Parser.DetachDocRoot();
}

bool WMSOAPSerializer::SerializeBase64Vector(SAFEARRAY *pArray, WMXMLElement *pOutParam)
{
	unsigned char *pData = NULL;
	SafeArrayAccessData(pArray, (void **)&pData);
	string strTemp;
	wstring wstrContent;
	WMBase64 base64;
	base64.Encode((const unsigned char *)pData, pArray->rgsabound[0].cElements, strTemp);
	UTF8ToWideString(strTemp, wstrContent);
	pOutParam->AppendContent(wstrContent);
	SafeArrayUnaccessData(pArray);
	return true;
}

bool WMSOAPSerializer::SerializeHexBinaryVector(SAFEARRAY *pArray, WMXMLElement *pOutParam)
{
	unsigned char *pData = NULL;
	SafeArrayAccessData(pArray, (void **)&pData);
	string strTemp;
	wstring wstrContent;
	WMHexCode hexCode;
	hexCode.Encode((const unsigned char *)pData, pArray->rgsabound[0].cElements, strTemp);
	UTF8ToWideString(strTemp, wstrContent);
	pOutParam->AppendContent(wstrContent);
	SafeArrayUnaccessData(pArray);
	return true;
}

bool WMSOAPSerializer::SerializeSafeArray(  long lDim,
										    long *arrIndex,
										    SAFEARRAY *pArray,
										    WMXMLElement *pOutParam,
											BSTR bstrType,
											BSTR bstrTypeNS,
                                            const wstring& wstrEncStyle,
											WMSOAPMessage& Msg,
										    Iwmwsdlclient2 *pIWSDLClient)
{
	long lNumDimsUpperBound = pArray->cDims - 1;
	for (unsigned long i = pArray->rgsabound[lDim].lLbound;
			i < pArray->rgsabound[lDim].cElements + pArray->rgsabound[lDim].lLbound; i++)
	{
		// leftmost dimension in arrIndex[numDims - 1]
		arrIndex[lNumDimsUpperBound - lDim] = i;
		if (lDim == lNumDimsUpperBound)
		{
			// bottomed out
			_variant_t varTemp;
			if (FAILED (SafeArrayGetElement(pArray, arrIndex, &varTemp)))
			{
				throw new WMSOAPFault(WMSOAPFault::Receiver, L"Error while serializing array parameter.", true);
			}
			switch (varTemp.vt)
			{
				case  (VT_ARRAY | VT_VARIANT):
				{
					// get SAFEARRAY pointer
					SAFEARRAY *pArray = varTemp.parray;
					if (pArray == NULL)
					{
						throw new WMSOAPFault(WMSOAPFault::Receiver, L"Error while serializing 'array of array' parameter.", true);
					}
					// look up data type in WSDL
					BSTR bstrArrayType, bstrArrayTypeNS;
					VARIANT_BOOL bResult;
					pIWSDLClient->GetArrayType( bstrType,
												bstrTypeNS,
												&bstrArrayType,
												&bstrArrayTypeNS,
												&bResult);
					if (bResult == VARIANT_FALSE)
					{
						// not properly defined in WSDL file
						throw new WMSOAPFault(WMSOAPFault::Receiver, L"Error while serializing 'array of array' parameter.", true);
					}
					// create element
					WMXMLElement *pElem = new WMXMLElement(wstring(L"item"));
					pOutParam->AddChildElement(pElem);
					// extract values from array
					long lNumDims = SafeArrayGetDim(pArray);
					long *arrIndex = new long[lNumDims];
					SerializeSafeArray( 0,
										arrIndex,
										pArray,
										pElem,
										bstrArrayType,
										bstrArrayTypeNS,
                                        wstrEncStyle,
										Msg,
										pIWSDLClient);
					delete [] arrIndex;
					wstring wstrArrayType(bstrArrayType);
					wstring wstrArrayTypeNS(bstrArrayTypeNS);
					SysFreeString(bstrArrayType);
					SysFreeString(bstrArrayTypeNS);
			        // output array attributes
                    if (wstrEncStyle.find(g_pwszSoapEncodingNS) == 0)
                    {
                        // SOAP 1.1 encoding
					    wstring wstrTemp = L"nsa:";
					    wstrArrayType = wstrTemp + wstrArrayType;
					    wstrTemp = L"[";
					    for (int k = 0; k < lNumDims; k++)
					    {
						    wchar_t wszBuf[16];
						    if (k > 0)
						    {
							    _snwprintf(wszBuf, 15, L",%d", pArray->rgsabound[k].cElements + pArray->rgsabound[k].lLbound);
						    }
						    else
						    {
							    _snwprintf(wszBuf, 15, L"%d", pArray->rgsabound[k].cElements + pArray->rgsabound[k].lLbound);
						    }
                            wszBuf[15] = 0;
						    wstrTemp += wszBuf;
					    }
					    wstrTemp += L"]";
					    pElem->SetAttribute(wstring(L"SOAP-ENC:arrayType"),
										    wstring(g_pwszSoapEncodingNS),
										    wstrArrayType + wstrTemp);
					    // output offsets attribute
					    wstrTemp = L"[";
					    for (k = 0; k < lNumDims; k++)
					    {
						    wchar_t wszBuf[16];
						    if (k > 0)
						    {
							    _snwprintf(wszBuf, 15, L",%d", pArray->rgsabound[k].lLbound);
						    }
						    else
						    {
							    _snwprintf(wszBuf, 15, L"%d", pArray->rgsabound[k].lLbound);
						    }
                            wszBuf[15] = 0;
						    wstrTemp += wszBuf;
					    }
					    wstrTemp += L"]";
					    pElem->SetAttribute(wstring(L"SOAP-ENC:offset"),
										    wstring(g_pwszSoapEncodingNS),
										    wstrTemp);
                    }
                    else // SOAP 1.2 encoding
                    {
    			        // output itemType attribute
			            wstring wstrTemp = L"nsa:";
			            wstrArrayType = wstrTemp + wstrArrayType;
			            pElem->SetAttribute(wstring(L"SOAP-ENC:itemType"),
								            wstring(g_pwszSoap12EncodingNS),
								            wstrArrayType);
			            // output arraySize attribute
			            wstrTemp = L"";
			            for (int k = 0; k < lNumDims; k++)
			            {
				            wchar_t wszBuf[16];
				            if (k > 0)
				            {
					            _snwprintf(wszBuf, 15, L" %d", pArray->rgsabound[k].cElements + pArray->rgsabound[k].lLbound);
				            }
				            else
				            {
					            _snwprintf(wszBuf, 15, L"%d", pArray->rgsabound[k].cElements + pArray->rgsabound[k].lLbound);
				            }
                            wszBuf[15] = 0;
				            wstrTemp += wszBuf;
			            }
			            pElem->SetAttribute(wstring(L"SOAP-ENC:arraySize"),
								            wstring(g_pwszSoap12EncodingNS),
								            wstrTemp);
                    }
					pElem->SetNSPrefix(wstring(L"nsa"), wstrArrayTypeNS);
					if (Msg.ExplicitTypingEnabled() == true)
					{
						pElem->SetAttribute(wstring(L"xsi:type"), wstring(g_pwszXMLSchemaInstNS2001), wstring(L"SOAP-ENC:Array"));
					}
                    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_2 && Msg.NodeTypeEnabled() == true)
                    {
                        pElem->SetAttribute(wstring(L"SOAP-ENC:nodeType"), wstring(g_pwszSoap12EncodingNS), wstring(L"array"));
                    }
					break;
				}
				case  (VT_DISPATCH):
				{
					// SOAP-ENC:Struct
					WMXMLElement *pElem = new WMXMLElement(wstring(L"item"));
					pOutParam->AddChildElement(pElem);
					IDispatch *pIDispatch = varTemp.pdispVal;
					if (SerializeStruct(bstrType,
										bstrTypeNS,
										pIDispatch,
										pElem,
                                        wstrEncStyle,
										Msg,
										pIWSDLClient) == false)
					{
						// trouble
						throw new WMSOAPFault(WMSOAPFault::Receiver, L"Error while serializing 'array of struct' parameter.", true);
					}
					break;
				}
				default:
				{
					// built-in type
					BSTR bstrName = SysAllocString(L"item");
					WMXMLElement *pElem = SerializeBuiltInType( bstrName,
																wstring(bstrType),
																wstring(bstrTypeNS),
																varTemp,
                                                                wstrEncStyle,
																Msg);
					SysFreeString(bstrName);
					if (pElem == NULL)
					{
						// trouble
						throw new WMSOAPFault(WMSOAPFault::Receiver, L"Error while serializing 'array' parameter.", true);
					}
					pOutParam->AddChildElement(pElem);
					break;
				}
			}
			varTemp.Clear();
		}
		else
		{
			// recursive call...in direction of rightmost dimension
			SerializeSafeArray( lDim + 1,
								arrIndex,
								pArray,
								pOutParam,
								bstrType,
								bstrTypeNS,
                                wstrEncStyle,
								Msg,
								pIWSDLClient);
		}
	}
	return true;
}

bool WMSOAPSerializer::SerializeSafeArrayLit(   SAFEARRAY *pArray,
										        WMXMLElement *pOutParam,
											    const wstring& wstrItemType,
											    const wstring& wstrItemTypeNS,
                                                const wstring& wstrItemName,
                                                const wstring& wstrItemNS,
											    WMSOAPMessage& Msg,
										        Iwmwsdlclient2 *pIWSDLClient)
{
    // single dimensional arrays only
    if (pArray->cDims != 1)
    {
		throw new WMSOAPFault(WMSOAPFault::Receiver, L"Error while serializing literal array parameter.", true);
    }
    long lIndex;
	for (unsigned long i = pArray->rgsabound[0].lLbound;
			i < pArray->rgsabound[0].cElements + pArray->rgsabound[0].lLbound; i++)
	{
		lIndex = i;
		_variant_t varTemp;
		if (FAILED (SafeArrayGetElement(pArray, &lIndex, &varTemp)))
		{
			throw new WMSOAPFault(WMSOAPFault::Receiver, L"Error while serializing literal array parameter.", true);
		}
		switch (varTemp.vt)
		{
			case  (VT_ARRAY | VT_VARIANT):
			{
				// get SAFEARRAY pointer
				SAFEARRAY *pArray = varTemp.parray;
				if (pArray == NULL)
				{
					throw new WMSOAPFault(WMSOAPFault::Receiver, L"Error while serializing 'array of array' literal parameter.", true);
				}
				// look up data type in WSDL
				BSTR bstrChildItemType, bstrChildItemTypeNS, bstrChildItemName, bstrChildItemNS;
				VARIANT_BOOL bResult;
                BSTR bstrType = SysAllocString(wstrItemType.c_str());
                BSTR bstrTypeNS = SysAllocString(wstrItemTypeNS.c_str());
				pIWSDLClient->GetArrayTypeLit(  bstrType,
												bstrTypeNS,
												&bstrChildItemType,
												&bstrChildItemTypeNS,
                                                &bstrChildItemName,
                                                &bstrChildItemNS,
												&bResult);
                SysFreeString(bstrType);
                SysFreeString(bstrTypeNS);
				if (bResult == VARIANT_FALSE)
				{
					// not properly defined in WSDL file
					throw new WMSOAPFault(WMSOAPFault::Receiver, L"Error while serializing 'array of array' literal parameter.", true);
				}
                wstring wstrChildItemType(bstrChildItemType);
                wstring wstrChildItemTypeNS(bstrChildItemTypeNS);
                wstring wstrChildItemName(bstrChildItemName);
                wstring wstrChildItemNS(bstrChildItemNS);
                SysFreeString(bstrChildItemType);
                SysFreeString(bstrChildItemTypeNS);
                SysFreeString(bstrChildItemName);
                SysFreeString(bstrChildItemNS);
				// create element
                WMXMLElement *pElem;
                if (wstrItemNS.empty() == true)
                {
	                pElem = new WMXMLElement(wstrItemName);
                }
                else
                {
                    wstring wstrTemp(L"nsa:");
                    wstrTemp += wstrItemName;
	                pElem = new WMXMLElement(wstrTemp);
                    if (pElem != NULL)
                    {
                        pElem->SetNSPrefix(wstring(L"nsa"), wstrItemNS);
                    }
                }
				if (pElem == NULL)
				{
					throw new WMSOAPFault(WMSOAPFault::Receiver, L"Out of memory error while serializing 'array of array' literal parameter.", true);
				}
				pOutParam->AddChildElement(pElem);
				// extract values from array
				SerializeSafeArrayLit(  pArray,
										pElem,
										wstrChildItemType,
										wstrChildItemTypeNS,
                                        wstrChildItemName,
                                        wstrChildItemNS,
										Msg,
										pIWSDLClient);
				if (Msg.ExplicitTypingEnabled() == true)
				{
		            pElem->SetNSPrefix(wstring(L"ns"), wstring(bstrTypeNS));
		            wstring wstrTemp = L"ns:";
		            wstrTemp += bstrType;
		            pElem->SetAttribute(wstring(L"xsi:type"), wstring(g_pwszXMLSchemaInstNS2001), wstrTemp);
				}
				break;
			}
			case  (VT_DISPATCH):
			{
				// struct
                WMXMLElement *pElem;
                if (wstrItemNS.empty() == true)
                {
	                pElem = new WMXMLElement(wstrItemName);
                }
                else
                {
                    wstring wstrTemp(L"nss:");
                    wstrTemp += wstrItemName;
	                pElem = new WMXMLElement(wstrTemp);
                    if (pElem != NULL)
                    {
                        pElem->SetNSPrefix(wstring(L"nss"), wstrItemNS);
                    }
                }
				if (pElem == NULL)
				{
					throw new WMSOAPFault(WMSOAPFault::Receiver, L"Out of memory error while serializing 'array of struct' literal parameter.", true);
				}
				pOutParam->AddChildElement(pElem);
				IDispatch *pIDispatch = varTemp.pdispVal;
				if (SerializeStructLit( wstrItemType,
										wstrItemTypeNS,
										pIDispatch,
										pElem,
										Msg,
										pIWSDLClient) == false)
				{
					// trouble
					throw new WMSOAPFault(WMSOAPFault::Receiver, L"Error while serializing 'array of struct' literal parameter.", true);
				}
				break;
			}
			default:
			{
				// built-in type
				WMXMLElement *pElem = SerializeBuiltInTypeLit(  wstrItemName,
                                                                wstrItemNS,
																wstrItemType,
																wstrItemTypeNS,
																varTemp,
																Msg);
				if (pElem == NULL)
				{
					// trouble
					throw new WMSOAPFault(WMSOAPFault::Receiver, L"Error while serializing 'array of simple type' literal parameter.", true);
				}
				pOutParam->AddChildElement(pElem);
				break;
			}
		}
		varTemp.Clear();
	}
	return true;
}

bool WMSOAPSerializer::SerializeStruct(BSTR bstrStructType,
									   BSTR bstrStructTypeNS,
									   IDispatch *pIDispatch,
									   WMXMLElement *pOutParam,
                                       const wstring& wstrEncStyle,
									   WMSOAPMessage& Msg,
									   Iwmwsdlclient2 *pIWSDLClient)
{
	// now get Iwmsoapstruct pointer
	Iwmsoapstruct *pIwmsoapstruct;
	HRESULT res = pIDispatch->QueryInterface(IID_Iwmsoapstruct, (void **)&pIwmsoapstruct);
	if (FAILED(res))
	{
		return false;
	}
	_variant_t varContent;
	BSTR bstrMemberName, bstrMemberNS, bstrType, bstrTypeNS;

	long lPos;
	VARIANT_BOOL bResult;
	for (   pIwmsoapstruct->GetFirstMemberQN(&bstrMemberName, &bstrMemberNS, &varContent, &lPos, &bResult);
			bResult == VARIANT_TRUE;
			pIwmsoapstruct->GetNextMemberQN(&bstrMemberName, &bstrMemberNS, &varContent, &lPos, &bResult))
	{
		// what is type?
		pIWSDLClient->GetStructMemberType(  bstrStructType,
											bstrStructTypeNS,
											bstrMemberName,
											bstrMemberNS,
											&bstrType,
											&bstrTypeNS,
											&bResult);

		if (bResult == VARIANT_FALSE)
		{
			// not properly defined in WSDL file
			SysFreeString(bstrMemberName);
			SysFreeString(bstrMemberNS);
			pIwmsoapstruct->Release();
			return false;
		}
		wstring wstrType(bstrType);
		wstring wstrTypeNS(bstrTypeNS);
		// does member have a qualified name?
		bool bIsQName = false;
		if (SysStringLen(bstrMemberNS) > 0)
		{
			// yes, so prefix it
			BSTR bstrPrefix = SysAllocString(L"nsm:");
			BSTR bstrTemp;
			VarBstrCat(bstrPrefix, bstrMemberName, &bstrTemp);
			SysFreeString(bstrMemberName);
            SysFreeString(bstrPrefix);
			bstrMemberName = bstrTemp;
			bIsQName = true;
		}
		WMXMLElement *pElem = NULL;
		// is this a built-in type?
		if (IsXSDType(wstrTypeNS) || IsSoapEncType(wstrTypeNS))
		{
			// built-in type
			pElem = SerializeBuiltInType(bstrMemberName,
										wstrType,
										wstrTypeNS,
										varContent,
                                        wstrEncStyle,
										Msg);
		}
		else
		{
			// complex type
			pElem = SerializeComplexType(bstrMemberName,
										bstrType,
										bstrTypeNS,
										varContent,
                                        wstrEncStyle,
										Msg,
										pIWSDLClient);
		}
		// free BSTRs
		SysFreeString(bstrMemberName);
		SysFreeString(bstrType);
		SysFreeString(bstrTypeNS);
		if (pElem == NULL)
		{
			// failed to serialize
			SysFreeString(bstrMemberNS);
			pIwmsoapstruct->Release();
			return false;
		}
		// clear variant
		varContent.Clear();
		// qname?
		if (bIsQName == true)
		{
			pElem->SetNSPrefix(wstring(L"nsm"), wstring(bstrMemberNS));
		}
		SysFreeString(bstrMemberNS);
		// append the element
		pOutParam->AddChildElement(pElem);
	}
	if (Msg.ExplicitTypingEnabled() == true)
	{
		pOutParam->SetNSPrefix(wstring(L"nss"), wstring(bstrStructTypeNS));
		wstring wstrTemp = L"nss:";
		wstrTemp += bstrStructType;
		pOutParam->SetAttribute(wstring(L"xsi:type"), wstring(g_pwszXMLSchemaInstNS2001), wstrTemp);
	}
    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_2 && Msg.NodeTypeEnabled() == true)
    {
        pOutParam->SetAttribute(wstring(L"SOAP-ENC:nodeType"), wstring(g_pwszSoap12EncodingNS), wstring(L"struct"));
    }
	// release
	pIwmsoapstruct->Release();
	return true;
}

bool WMSOAPSerializer::SerializeStructLit(  const wstring& wstrStructType,
									        const wstring& wstrStructTypeNS,
									        IDispatch *pIDispatch,
									        WMXMLElement *pOutParam,
									        WMSOAPMessage& Msg,
									        Iwmwsdlclient2 *pIWSDLClient)
{
	// now get Iwmsoapstruct pointer
	Iwmsoapstruct *pIwmsoapstruct;
	HRESULT res = pIDispatch->QueryInterface(IID_Iwmsoapstruct, (void **)&pIwmsoapstruct);
	if (FAILED(res))
	{
		return false;
	}
	_variant_t varContent;
	BSTR bstrMemberName, bstrMemberNS, bstrType, bstrTypeNS;
    BSTR bstrStructType = SysAllocString(wstrStructType.c_str());
    BSTR bstrStructTypeNS = SysAllocString(wstrStructTypeNS.c_str());
	
	long lPos;
	VARIANT_BOOL bResult;
	for (   pIwmsoapstruct->GetFirstMemberQN(&bstrMemberName, &bstrMemberNS, &varContent, &lPos, &bResult);
			bResult == VARIANT_TRUE;
			pIwmsoapstruct->GetNextMemberQN(&bstrMemberName, &bstrMemberNS, &varContent, &lPos, &bResult))
	{
		// what is type?
		pIWSDLClient->GetStructMemberType(  bstrStructType,
											bstrStructTypeNS,
											bstrMemberName,
											bstrMemberNS,
											&bstrType,
											&bstrTypeNS,
											&bResult);
		if (bResult == VARIANT_FALSE)
		{
			// not properly defined in WSDL file
            SysFreeString(bstrStructType);
            SysFreeString(bstrStructTypeNS);
			SysFreeString(bstrMemberName);
			SysFreeString(bstrMemberNS);
			pIwmsoapstruct->Release();
			return false;
		}
		wstring wstrType(bstrType);
		wstring wstrTypeNS(bstrTypeNS);
        wstring wstrMemberName(bstrMemberName);
        wstring wstrMemberNS(bstrMemberNS);
		SysFreeString(bstrMemberName);
		SysFreeString(bstrMemberNS);
		WMXMLElement *pElem = NULL;
		// is this a built-in type?
		if (IsXSDType(wstrTypeNS) || IsSoapEncType(wstrTypeNS))
		{
			// built-in type
			pElem = SerializeBuiltInTypeLit(wstrMemberName,
                                            wstrMemberNS,
										    wstrType,
										    wstrTypeNS,
										    varContent,
										    Msg);
		}
		else
		{
			// complex type
			pElem = SerializeComplexTypeLit(wstrMemberName,
                                            wstrMemberNS,
										    bstrType,
										    bstrTypeNS,
										    varContent,
										    Msg,
										    pIWSDLClient);
		}
		SysFreeString(bstrType);
		SysFreeString(bstrTypeNS);
		if (pElem == NULL)
		{
			// failed to serialize
            SysFreeString(bstrStructType);
            SysFreeString(bstrStructTypeNS);
			pIwmsoapstruct->Release();
			return false;
		}
		// clear variant
		varContent.Clear();
		// append the element
		pOutParam->AddChildElement(pElem);
	}
    SysFreeString(bstrStructType);
    SysFreeString(bstrStructTypeNS);
	if (Msg.ExplicitTypingEnabled() == true)
	{
		pOutParam->SetNSPrefix(wstring(L"nss"), wstrStructTypeNS);
		wstring wstrTemp = L"nss:";
		wstrTemp += wstrStructType;
		pOutParam->SetAttribute(wstring(L"xsi:type"), wstring(g_pwszXMLSchemaInstNS2001), wstrTemp);
	}
	// release
	pIwmsoapstruct->Release();
	return true;
}

bool WMSOAPSerializer::SerializeDateTime(DATE d, WMXMLElement *pOutParam)
{
	struct tm t;
	if (TmFromOleDate(d, t) == false)
	{
		return false;
	}
	TmConvertToStandardFormat(t);
	// format
	wchar_t wszBuffer[64];
	wcsftime(wszBuffer, 64, L"%Y-%m-%dT%H:%M:%SZ", &t);
	pOutParam->AppendContent(wstring(wszBuffer));
	return true;
}

bool WMSOAPSerializer::SerializeDate(DATE d, WMXMLElement *pOutParam)
{
	struct tm t;
	if (TmFromOleDate(d, t) == false)
	{
		return false;
	}
	TmConvertToStandardFormat(t);
	// format
	wchar_t wszBuffer[64];
	wcsftime(wszBuffer, 64, L"%Y-%m-%d", &t);
	pOutParam->AppendContent(wstring(wszBuffer));
	return true;
}

bool WMSOAPSerializer::SerializeTime(DATE d, WMXMLElement *pOutParam)
{
	struct tm t;
	if (TmFromOleDate(d, t) == false)
	{
		return false;
	}
	TmConvertToStandardFormat(t);
	// format
	wchar_t wszBuffer[64];
	wcsftime(wszBuffer, 64, L"%H:%M:%SZ", &t);
	pOutParam->AppendContent(wstring(wszBuffer));
	return true;
}

wstring WMSOAPSerializer::GenerateId()
{
	GUID g;
	CoCreateGuid(&g);
	OLECHAR wszBuffer1[64];
	OLECHAR wszBuffer2[64];
	StringFromGUID2(g, wszBuffer1, 64);
	wszBuffer1[37] = 0;
	swprintf(wszBuffer2, L"uuid:%s", &wszBuffer1[1]);
	return wstring(wszBuffer2);
}
