// WMSOAPDeserializer.cpp: implementation of the WMSOAPDeserializer 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.
//
//////////////////////////////////////////////////////////////////////
//
// WMSOAPDeserializer class - a SOAP message deserializer base class.
// Both "encoded" (SOAP encoding only) or "literal" usage supported.
// 
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "WMSOAPDeserializer.h"
#include "WMXMLMessage.h"
#include "base64.h"
#include "hexcode.h"
#include "..\wmwsdlclient\wmwsdlclient.h"
#include "..\wmsoapstruct\wmsoapstruct.h"
#include "..\wmsoapstruct\wmsoapstruct_i.c"

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

WMSOAPDeserializer::WMSOAPDeserializer()
{
    // 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));
}

WMSOAPDeserializer::~WMSOAPDeserializer()
{
}

IDispatch *WMSOAPDeserializer::DeserializeStructEnc(const wstring& wstrEncStyle,
                                                    BSTR bstrStructName,
                                                    BSTR bstrStructNS,
                                                    WMSOAPParamHandler *pParam,
                                                    WMSOAPMessage& Msg,
                                                    Iwmwsdlclient2 *pIWSDLClient)
{
    if (pParam->CheckEncodingStyle(wstrEncStyle) == false)
    {
        wchar_t *pwszMsg = L"Unsupported encodingStyle on struct accessor.";
        if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
        {
		    throw new WMSOAPFault(  WMSOAPFault::Sender, pwszMsg, true);
        }
        else
        {
		    throw new WMSOAPFault(  WMSOAPFault::DataEncodingUnknown, pwszMsg, true);
        }
    }
    // must have nodeType="struct"
    if (pParam->NodeTypeAsserted() == true)
    {
        if (pParam->GetNodeType() != L"struct")
        {
		    throw new WMSOAPFault(  WMSOAPFault::Sender, L"Incorrect nodeType on struct accessor.", true);
        }
    }
    // instantiate struct object
    Iwmsoapstruct *pIwmsoapstruct;
    HRESULT res = CoCreateInstance( CLSID_wmsoapstruct,
                            NULL, CLSCTX_SERVER,
                            IID_Iwmsoapstruct,
                            (void **) &pIwmsoapstruct);
    if (FAILED(res))
    {
		throw new WMSOAPFault(  WMSOAPFault::Receiver, L"Internal error.", true);
    }
    // content
    _variant_t varContent;
    for (unsigned int i = 0; i < pParam->GetChildren().size(); i++)
    {
        // initialize
        varContent.Clear();
        // what is type?
        BSTR bstrMemberName, bstrMemberNS, bstrType, bstrTypeNS;
        WMSOAPParamHandler *pMember = pParam->GetChildren()[i];
        bstrMemberName = SysAllocString(pMember->GetLocalName().c_str());
        bstrMemberNS = SysAllocString(pMember->GetURI().c_str());
        VARIANT_BOOL bResult;
        pIWSDLClient->GetStructMemberType(  bstrStructName,
                                            bstrStructNS,
                                            bstrMemberName,
                                            bstrMemberNS,
                                            &bstrType,
                                            &bstrTypeNS,
                                            &bResult);

        if (bResult == VARIANT_FALSE)
        {
            // not properly defined in WSDL file
            pIwmsoapstruct->Release();
            SysFreeString(bstrMemberName);
            SysFreeString(bstrMemberNS);
		    throw new WMSOAPFault(WMSOAPFault::Sender, L"Encountered struct member accessor with unrecognized type.", true);
        }
        wstring wstrType(bstrType);
        wstring wstrTypeNS(bstrTypeNS);
        // is this a multi-ref?
        bool bIsRef = false;
        bool bIsAttachment = false;
        WMSOAPParamHandler *pRefValue = pMember;
        if (pMember->IsRef() == true)
        {
            bIsRef = true;
            pRefValue = GetMultiRefElem(pMember);
            if (pRefValue == NULL)
            {
                // is it a ref to a DIME attachment?
                bIsAttachment = GetDIMEAttachment(  Msg,
                                                    pIWSDLClient,
                                                    pMember,
                                                    bstrType,
                                                    bstrTypeNS,
                                                    varContent);
                if (bIsAttachment == false)
                {
                    // we have encountered a bad reference
                    pIwmsoapstruct->Release();
                    SysFreeString(bstrMemberName);
                    SysFreeString(bstrMemberNS);
                    SysFreeString(bstrType);
                    SysFreeString(bstrTypeNS);
                    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
                    {
			            throw new WMSOAPFault(  WMSOAPFault::Sender,
                                                L"Encountered struct member accessor with invalid reference.",
                                                true);
                    }
                    else
                    {
                        // SOAP 1.2
			            throw new WMSOAPFault(  WMSOAPFault::Sender,
                                                L"MissingID",
                                                g_pwszSoap12EncodingNS,
                                                L"Encountered struct member accessor with invalid reference.",
                                                true);
                    }
                }
            }
        }
        // if not an attachment we need to deserialize
        if (bIsAttachment == false)
        {
            try
            {
                // built-in type?
                if (IsXSDType(wstrTypeNS) || IsSoapEncType(wstrTypeNS))
                {
                    DeserializeBuiltInTypeEnc(  wstrEncStyle,
                                                Msg,
                                                pRefValue,
                                                wstrType,
                                                varContent);
                }
                else
                {
                    // complex type
                    DeserializeComplexTypeEnc(  wstrEncStyle,
                                                Msg,
                                                pIWSDLClient,
                                                pRefValue,
                                                pMember,
                                                bIsRef,
                                                bstrType,
                                                bstrTypeNS,
                                                varContent);
                }
            }
            catch (WMSOAPFault *f)
            {
                pIwmsoapstruct->Release();
                SysFreeString(bstrMemberName);
                SysFreeString(bstrMemberNS);
                SysFreeString(bstrType);
                SysFreeString(bstrTypeNS);
                throw f;
            }
        }
        pIwmsoapstruct->SetMemberQN(bstrMemberName, bstrMemberNS, varContent, &bResult);
        SysFreeString(bstrMemberName);
        SysFreeString(bstrMemberNS);
        SysFreeString(bstrType);
        SysFreeString(bstrTypeNS);
    }
    // now get IDispatch pointer
    IDispatch *pIDispatch;
    res = pIwmsoapstruct->QueryInterface(IID_IDispatch, (void **)&pIDispatch);
    if (FAILED(res))
    {
        pIwmsoapstruct->Release();
		throw new WMSOAPFault(  WMSOAPFault::Receiver, L"Internal error.", true);
    }
    // release this pointer
    pIwmsoapstruct->Release();
    return pIDispatch;
}

IDispatch *WMSOAPDeserializer::DeserializeStructLit(BSTR bstrStructName,
                                                    BSTR bstrStructNS,
                                                    WMSOAPParamHandler *pParam,
                                                    WMSOAPMessage& Msg,
                                                    Iwmwsdlclient2 *pIWSDLClient)
{
    // instantiate struct object
    Iwmsoapstruct *pIwmsoapstruct;
    HRESULT res = CoCreateInstance( CLSID_wmsoapstruct,
                            NULL, CLSCTX_SERVER,
                            IID_Iwmsoapstruct,
                            (void **) &pIwmsoapstruct);
    if (FAILED(res))
    {
		throw new WMSOAPFault(  WMSOAPFault::Receiver, L"Internal error.", true);
    }
    // content
    _variant_t varContent;
    for (unsigned int i = 0; i < pParam->GetChildren().size(); i++)
    {
        // initalize
        varContent.Clear();
        // what is type?
        BSTR bstrMemberName, bstrMemberNS, bstrType, bstrTypeNS;
        WMSOAPParamHandler *pMember = pParam->GetChildren()[i];
        bstrMemberName = SysAllocString(pMember->GetLocalName().c_str());
        bstrMemberNS = SysAllocString(pMember->GetURI().c_str());
        VARIANT_BOOL bResult;
        pIWSDLClient->GetStructMemberType(  bstrStructName,
                                            bstrStructNS,
                                            bstrMemberName,
                                            bstrMemberNS,
                                            &bstrType,
                                            &bstrTypeNS,
                                            &bResult);

        if (bResult == VARIANT_FALSE)
        {
            // not properly defined in WSDL file
            pIwmsoapstruct->Release();
            SysFreeString(bstrMemberName);
            SysFreeString(bstrMemberNS);
		    throw new WMSOAPFault(WMSOAPFault::Sender, L"Encountered struct member accessor with unrecognized type.", true);
        }
        wstring wstrType(bstrType);
        wstring wstrTypeNS(bstrTypeNS);
        try
        {
            // built-in type?
            if (IsXSDType(wstrTypeNS) || IsSoapEncType(wstrTypeNS))
            {
                DeserializeBuiltInTypeLit(  Msg,
                                            pMember,
                                            wstrType,
                                            varContent);
            }
            else
            {
                // complex type
                DeserializeComplexTypeLit(  Msg,
                                            pIWSDLClient,
                                            pMember,
                                            bstrType,
                                            bstrTypeNS,
                                            varContent);
            }
        }
        catch (WMSOAPFault *f)
        {
            pIwmsoapstruct->Release();
            SysFreeString(bstrMemberName);
            SysFreeString(bstrMemberNS);
            SysFreeString(bstrType);
            SysFreeString(bstrTypeNS);
            throw f;
        }
        pIwmsoapstruct->SetMemberQN(bstrMemberName, bstrMemberNS, varContent, &bResult);
        SysFreeString(bstrMemberName);
        SysFreeString(bstrMemberNS);
        SysFreeString(bstrType);
        SysFreeString(bstrTypeNS);
    }
    // now get IDispatch pointer
    IDispatch *pIDispatch;
    res = pIwmsoapstruct->QueryInterface(IID_IDispatch, (void **)&pIDispatch);
    if (FAILED(res))
    {
        pIwmsoapstruct->Release();
		throw new WMSOAPFault(  WMSOAPFault::Receiver, L"Internal error.", true);
    }
    // release this pointer
    pIwmsoapstruct->Release();
    return pIDispatch;
}

SAFEARRAY *WMSOAPDeserializer::DeserializeBase64Vector(WMSOAPParamHandler *pParam)
{
    string strTemp;
    WideStringToUTF8(pParam->GetCharContent(), strTemp);
    // strip out any whitespace
    string strBuffer;
    const char *p = strTemp.c_str();
    while (*p)
    {
        if (!isspace(*p))
        {
            strBuffer += *p;
        }
        ++p;
    }
    // get required buffer size
    int nLength = 0;
    WMBase64 base64;
    if (base64.Decode(strBuffer, NULL, nLength) == false)
    {
        return NULL;
    }
    unsigned char *pszOutBuffer = (unsigned char *)malloc(nLength);
    if (pszOutBuffer == NULL || base64.Decode(strBuffer, pszOutBuffer, nLength) == false)
    {
        if (pszOutBuffer != NULL)
        {
            free(pszOutBuffer);
        }
        return NULL;
    }
    // now create vector
    SAFEARRAY *pArray = SafeArrayCreateVector(VT_UI1, 0L, nLength);
    if (pArray == NULL)
    {
        free(pszOutBuffer);
        return NULL;
    }
    void *pData = NULL;
    if (FAILED(SafeArrayAccessData(pArray, &pData)))
    {
        free(pszOutBuffer);
        SafeArrayDestroy(pArray);
        return NULL;
    }
    // copy data into vector
    memmove(pData, pszOutBuffer, nLength);
    SafeArrayUnaccessData(pArray);
    free(pszOutBuffer);
    return pArray;
}

SAFEARRAY *WMSOAPDeserializer::DeserializeHexBinaryVector(WMSOAPParamHandler *pParam)
{
    string strTemp;
    WideStringToUTF8(pParam->GetCharContent(), strTemp);
    // strip out any whitespace
    string strBuffer;
    const char *p = strTemp.c_str();
    while (*p)
    {
        if (!isspace(*p))
        {
            // check for lowercase chars
            if (islower(*p))
            {
                // convert to canonical form
                strBuffer += (char)toupper(*p);
            }
            else
            {
                strBuffer += *p;
            }
        }
        ++p;
    }
    // get required buffer size
    int nLength = 0;
    WMHexCode hexCode;
    if (hexCode.Decode(strBuffer, NULL, nLength) == false)
    {
        return NULL;
    }
    unsigned char *pszOutBuffer = (unsigned char *)malloc(nLength);
    if (pszOutBuffer == NULL || hexCode.Decode(strBuffer, pszOutBuffer, nLength) == false)
    {
        if (pszOutBuffer != NULL)
        {
            free(pszOutBuffer);
        }
        return NULL;
    }
    // now create vector
    SAFEARRAY *pArray = SafeArrayCreateVector(VT_UI1, 0L, nLength);
    if (pArray == NULL)
    {
        free(pszOutBuffer);
        return NULL;
    }
    void *pData = NULL;
    if (FAILED(SafeArrayAccessData(pArray, &pData)))
    {
        free(pszOutBuffer);
        SafeArrayDestroy(pArray);
        return NULL;
    }
    // copy data into vector
    memmove(pData, pszOutBuffer, nLength);
    SafeArrayUnaccessData(pArray);
    free(pszOutBuffer);
    return pArray;
}

bool WMSOAPDeserializer::LoadSafeArrayEnc(const wstring& wstrEncStyle,
                                          long lDim,
                                          long *arrIndex,
                                          SAFEARRAY *pArray,
                                          VARTYPE VarType,
                                          long *plCounter,
                                          WMSOAPParamHandler *pParam,
                                          BSTR bstrType,
                                          BSTR bstrTypeNS,
                                          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;
            WMSOAPParamHandler *pElem = pParam->GetChildren()[*plCounter];
            // is this a multi-ref?
            bool bIsRef = false;
            bool bIsAttachment = false;
            WMSOAPParamHandler *pRefValue = pElem;
            if (pElem->IsRef() == true)
            {
                bIsRef = true;
                pRefValue = GetMultiRefElem(pElem);
                if (pRefValue == NULL)
                {
                    // is it a ref to a DIME attachment?
                    bIsAttachment = GetDIMEAttachment(  Msg,
                                                        pIWSDLClient,
                                                        pElem,
                                                        bstrType,
                                                        bstrTypeNS,
                                                        varTemp);
                    if (bIsAttachment == false)
                    {
                        // we have encountered a bad reference
                        if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
                        {
			                throw new WMSOAPFault(WMSOAPFault::Sender, L"Encountered array item accessor with invalid reference.", true);
                        }
                        else
                        {
                            // SOAP 1.2
			                throw new WMSOAPFault(  WMSOAPFault::Sender,
                                                    L"MissingID",
                                                    g_pwszSoap12EncodingNS,
                                                    L"Encountered array item accessor with invalid reference.",
                                                    true);
                        }
                    }
                }
            }
            // if not an attachment we need to deserialize
            if (bIsAttachment == false)
            {
                switch (VarType)
                {
                    case  (VT_ARRAY | VT_VARIANT):
                    {
                        // array
                        SAFEARRAY *pArray = NULL;
                        // is this a multi-ref?
                        if (bIsRef == true)
                        {
                            // transfer any SOAP-ENC:offset attribute information
                            if (pElem->GetOffset().empty() == false)
                            {
                                pRefValue->SetArrayOffsets(pElem->GetOffset());
                            }
                        }
                        pArray = DeserializeArrayEnc(   wstrEncStyle,
                                                        bstrType,
                                                        bstrTypeNS,
                                                        pRefValue,
                                                        Msg,
                                                        pIWSDLClient);
                        varTemp.vt = VarType;
                        varTemp.parray = pArray;
                        break;
                    }
                    case  (VT_DISPATCH):
                    {
                        // struct
                        IDispatch *pStruct = NULL;
                        pStruct = DeserializeStructEnc( wstrEncStyle,
                                                        bstrType,
                                                        bstrTypeNS,
                                                        pRefValue,
                                                        Msg,
                                                        pIWSDLClient);
                        varTemp.vt = VarType;
                        varTemp.pdispVal = pStruct;
                        break;
                    }
                    default:
                    {
                        // built-in type
                        DeserializeBuiltInTypeEnc(  wstrEncStyle,
                                                    Msg,
                                                    pRefValue,
                                                    wstring(bstrType),
                                                    varTemp);
                    }
                }
            }
            // now store it
            if (FAILED(SafeArrayPutElement(pArray, arrIndex, &varTemp)))
            {
			    throw new WMSOAPFault(WMSOAPFault::Receiver, L"Internal error.", true);
            }
            varTemp.Clear();
            (*plCounter)++;
        }
        else
        {
            // recursive call...in direction of rightmost dimension
            LoadSafeArrayEnc(   wstrEncStyle,
                                lDim + 1,
                                arrIndex,
                                pArray,
                                VarType,
                                plCounter,
                                pParam,
                                bstrType,
                                bstrTypeNS,
                                Msg,
                                pIWSDLClient);
        }
    }
    return true;
}

SAFEARRAY *WMSOAPDeserializer::DeserializeSafeArrayEnc( const wstring& wstrEncStyle,
                                                        WMSOAPParamHandler *pParam,
                                                        VARTYPE VarType,
                                                        BSTR bstrType,
                                                        BSTR bstrTypeNS,
                                                        long lNumDims,
                                                        WMSOAPMessage& Msg,
                                                        Iwmwsdlclient2 *pIWSDLClient)
{
    unsigned long ulArraySize = pParam->GetChildren().size();
    // if nonzero asize given then verify against actual
    if (pParam->GetNumElems() >= 0)
    {
        if (pParam->GetNumElems() != ulArraySize)
        {
		    throw new WMSOAPFault(  WMSOAPFault::Sender, L"Array size declared conflicts with actual size.", true);
        }
    }
    SAFEARRAYBOUND *pBounds;
    pBounds = new SAFEARRAYBOUND[lNumDims];
    // check for nil array
    if (pParam->IsNil() == true)
    {
        for (long j = 0; j < lNumDims; j++)
        {
            int nIndex = (lNumDims - 1) - j;
            pBounds[nIndex].lLbound = 0;
            pBounds[nIndex].cElements = 0;
        }
    }
    // check for empty asize (e.g. 'xsd:int[]')
    else if (pParam->GetNumElems() == -1)
    {
        // reverse major ordering - SAFEARRAY is the reverse of SOAP-ENC
        // in terms of the SAFEARRAYBOUND array passed to SafeArrayCreate()
        for (long j = 0; j < lNumDims; j++)
        {
            int nIndex = (lNumDims - 1) - j;
            // default
            pBounds[nIndex].lLbound = 0L;
            pBounds[nIndex].cElements = pParam->GetArrayDims()[j];
            if (j == 0)
            {
                // SOAP-ENC:offset accepted for innermost dimension
                if (pParam->GetArrayOffsets().size() > 0)
                {
                    pBounds[nIndex].lLbound = pParam->GetArrayOffsets()[0];
                }
                // calculate count of elements for this dimension
                long lCount = 1;
                for (long k = 1; k < lNumDims; k++)
                {
                    lCount *= pParam->GetArrayDims()[k];
                }
                pBounds[nIndex].cElements = ulArraySize / lCount;
            }
        }
    }
    else
    {
        // reverse major ordering - SAFEARRAY is the reverse of SOAP-ENC
        // in terms of the SAFEARRAYBOUND array passed to SafeArrayCreate()
        for (long j = 0; j < lNumDims; j++)
        {
            int nIndex = (lNumDims - 1) - j;
            // SOAP-ENC:offset
            if (pParam->GetArrayOffsets().size() > 0)
            {
                pBounds[nIndex].lLbound = pParam->GetArrayOffsets()[j];
            }
            else
            {
                // default
                pBounds[nIndex].lLbound = 0L;
            }
            long lNumElems = pParam->GetArrayDims()[j];
            pBounds[nIndex].cElements = lNumElems - pBounds[nIndex].lLbound;
        }
    }
    SAFEARRAY *pArray = SafeArrayCreate(VT_VARIANT, lNumDims, pBounds);
    if (pArray == NULL)
    {
        delete [] pBounds;
		throw new WMSOAPFault(WMSOAPFault::Receiver, L"Internal error.", true);
    }
    // set up index variable and counter
    long *arrIndex = new long[lNumDims];
    long lCounter = 0;
    // now load array
    try
    {
        LoadSafeArrayEnc(   wstrEncStyle,
                            0,
                            arrIndex,
                            pArray,
                            VarType,
                            &lCounter,
                            pParam,
                            bstrType,
                            bstrTypeNS,
                            Msg,
                            pIWSDLClient);
    }
    catch(WMSOAPFault *f)
    {
        SafeArrayDestroy(pArray);
        delete[] arrIndex;
        delete[] pBounds;
        throw f;
    }
    // clean up
    delete[] arrIndex;
    delete[] pBounds;
    return pArray;
}

SAFEARRAY *WMSOAPDeserializer::DeserializeSafeArrayLit( WMSOAPParamHandler *pParam,
                                                        VARTYPE VarType,
                                                        BSTR bstrItemName,
                                                        BSTR bstrItemNS,
                                                        BSTR bstrItemType,
                                                        BSTR bstrItemTypeNS,
                                                        WMSOAPMessage& Msg,
                                                        Iwmwsdlclient2 *pIWSDLClient)
{
    unsigned long ulArraySize = pParam->GetChildren().size();
    SAFEARRAYBOUND Bounds;
    Bounds.lLbound = 0;
    Bounds.cElements = ulArraySize;
    SAFEARRAY *pArray = SafeArrayCreate(VT_VARIANT, 1, &Bounds);
    if (pArray == NULL)
    {
		throw new WMSOAPFault(WMSOAPFault::Receiver, L"Internal error.", true);
    }
    // set up index variable and counter
    _variant_t varTemp;
    long lIndex;
    try
    {
        for (unsigned long i = 0; i < ulArraySize; i++)
        {
            WMSOAPParamHandler *pElem = pParam->GetChildren()[i];
            lIndex = i;
            // is name of child element what we expect?
            if (pElem->GetURI() != bstrItemNS || pElem->GetLocalName() != bstrItemName)
            {
		        throw new WMSOAPFault(WMSOAPFault::Sender, L"Encountered unrecognized array item accessor.", true);
            }
            switch (VarType)
            {
                case  (VT_ARRAY | VT_VARIANT):
                {
			        BSTR bstrChildItemType, bstrChildItemTypeNS, bstrChildItemName, bstrChildItemNS;
                    VARIANT_BOOL bResult;
	                pIWSDLClient->GetArrayTypeLit(	bstrItemType,
								                    bstrItemTypeNS,
								                    &bstrChildItemType,
								                    &bstrChildItemTypeNS,
                                                    &bstrChildItemName,
                                                    &bstrChildItemNS,
								                    &bResult);
	                if (bResult == VARIANT_FALSE)
	                {
		                throw new WMSOAPFault(WMSOAPFault::Sender, L"Encountered array item accessor of unrecognized type.", true);
                    }
                    // array
                    SAFEARRAY *pArray = NULL;
                    try
                    {
                        pArray = DeserializeArrayLit(   pElem,
                                                        bstrChildItemName,
                                                        bstrChildItemNS,
                                                        bstrChildItemType,
                                                        bstrChildItemTypeNS,
                                                        Msg,
                                                        pIWSDLClient);
                    }
                    catch (WMSOAPFault *f)
                    {
                        SysFreeString(bstrChildItemType);
                        SysFreeString(bstrChildItemTypeNS);
                        SysFreeString(bstrChildItemName);
                        SysFreeString(bstrChildItemNS);
                        throw f;
                    }
                    SysFreeString(bstrChildItemType);
                    SysFreeString(bstrChildItemTypeNS);
                    SysFreeString(bstrChildItemName);
                    SysFreeString(bstrChildItemNS);
                    varTemp.vt = VarType;
                    varTemp.parray = pArray;
                    break;
                }
                case  (VT_DISPATCH):
                {
                    // struct
                    IDispatch *pStruct = NULL;
                    pStruct = DeserializeStructLit( bstrItemType,
                                                    bstrItemTypeNS,
                                                    pElem,
                                                    Msg,
                                                    pIWSDLClient);
                    varTemp.vt = VarType;
                    varTemp.pdispVal = pStruct;
                    break;
                }
                default:
                {
                    // built-in type
                    DeserializeBuiltInTypeLit(  Msg,
                                                pElem,
                                                wstring(bstrItemType),
                                                varTemp);
                    break;
                }
            }
            // now store it
            if (FAILED(SafeArrayPutElement(pArray, &lIndex, &varTemp)))
            {
		        throw new WMSOAPFault(WMSOAPFault::Receiver, L"Internal error.", true);
            }
            varTemp.Clear();
        }
    }
    catch (WMSOAPFault *f)
    {
        SafeArrayDestroy(pArray);
        throw f;
    }
    return pArray;
}

SAFEARRAY *WMSOAPDeserializer::DeserializeArrayEnc( const wstring& wstrEncStyle,
                                                    BSTR bstrArrayName,
                                                    BSTR bstrArrayNS,
                                                    WMSOAPParamHandler *pParam,
                                                    WMSOAPMessage& Msg,
                                                    Iwmwsdlclient2 *pIWSDLClient)
{
    if (pParam->CheckEncodingStyle(wstrEncStyle) == false)
    {
        wchar_t *pwszMsg = L"Unsupported encodingStyle on array accessor.";
        if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
        {
		    throw new WMSOAPFault(  WMSOAPFault::Sender, pwszMsg, true);
        }
        else
        {
		    throw new WMSOAPFault(  WMSOAPFault::DataEncodingUnknown, pwszMsg, true);
        }
    }
    // must have nodeType="array"
    if (pParam->NodeTypeAsserted() == true)
    {
        if (pParam->GetNodeType() != L"array")
        {
		    throw new WMSOAPFault(  WMSOAPFault::Sender, L"Incorrect nodeType on array accessor.", true);
        }
    }
    // look up expected array element simple data type in WSDL
    BSTR bstrType, bstrTypeNS;
    VARIANT_BOOL bResult;
    pIWSDLClient->GetArrayType( bstrArrayName,
                                bstrArrayNS,
                                &bstrType,
                                &bstrTypeNS,
                                &bResult);
    if (bResult == VARIANT_FALSE)
    {
		throw new WMSOAPFault(  WMSOAPFault::Sender, L"Encountered unrecognized array accessor.", true);
    }
    wstring wstrType(bstrType);
    wstring wstrTypeNS(bstrTypeNS);
    // if not nil then test array type
    if (pParam->IsNil() == false)
    {
        // SOAP 1.1 encoding?
        if (wstrEncStyle.find(g_pwszSoapEncodingNS) == 0)
        {
            // compare with type given in SOAP-ENC:arrayType attribute
            if (pParam->GetArrayElemType() != wstrType ||
                            pParam->GetArrayElemTypeNS() != wstrTypeNS)
            {
                // see if "anyType" is given as array type on wire
                if (pParam->GetArrayElemType() != L"anyType" &&
                    pParam->GetArrayElemType() != L"ur-type")
                {
                    // no match
                    SysFreeString(bstrType);
                    SysFreeString(bstrTypeNS);
		            throw new WMSOAPFault(  WMSOAPFault::Sender, L"Encountered improperly typed array accessor.", true);
                }
            }
        }
    }
    // look up type in map
    VARTYPE Vartype;
    map<wstring, VARTYPE>::iterator iV;
    // is this simple (intrinsic) part?
    if (IsXSDType(wstrTypeNS) || IsSoapEncType(wstrTypeNS))
    {
        if ((iV = m_mapVarTypes.find(wstrType)) != m_mapVarTypes.end())
        {
            // built-in type
            Vartype = (*iV).second;
        }
        else
        {
            // unrecognized type
            SysFreeString(bstrType);
            SysFreeString(bstrTypeNS);
		    throw new WMSOAPFault(  WMSOAPFault::Sender, L"Encountered unrecognized array item accessor type.", true);
        }
    }
    else
    {
        // must be a complex type so look for 'base' type
        BSTR bstrBaseType, bstrBaseTypeNS;
        pIWSDLClient->GetComplexBaseType(   bstrType,
                                            bstrTypeNS,
                                            &bstrBaseType,
                                            &bstrBaseTypeNS,
                                            &bResult);
        if (bResult == VARIANT_FALSE)
        {
            // not found
            SysFreeString(bstrType);
            SysFreeString(bstrTypeNS);
		    throw new WMSOAPFault(  WMSOAPFault::Sender, L"Encountered unrecognized array item accessor type.", true);
        }
        wstring wstrBaseType(bstrBaseType);
        wstring wstrBaseTypeNS(bstrBaseTypeNS);
        SysFreeString(bstrBaseType);
        SysFreeString(bstrBaseTypeNS);
        if (IsSoapEncType(wstrBaseTypeNS))
        {
            if (wstrBaseType == L"Array")
            {
                // SOAP-ENC:Array
                Vartype = VT_VARIANT | VT_ARRAY;
            }
            else if (wstrBaseType == L"Struct")
            {
                // SOAP-ENC:Struct
                Vartype = VT_DISPATCH;
            }
            else if (IsXSDType(wstrBaseTypeNS) || IsSoapEncType(wstrBaseTypeNS))
            {
                // is base type found in map?
                if ((iV = m_mapVarTypes.find(wstrBaseType)) != m_mapVarTypes.end())
                {
                    // yes, it's a built-in type
                    Vartype = (*iV).second;
                }
                else
                {
                    // no, unrecognized type
                    SysFreeString(bstrType);
                    SysFreeString(bstrTypeNS);
		            throw new WMSOAPFault(  WMSOAPFault::Sender, L"Encountered unrecognized array item accessor type.", true);
                }
            }
            else
            {
                // unrecognized SOAP-ENC type
                SysFreeString(bstrType);
                SysFreeString(bstrTypeNS);
		        throw new WMSOAPFault(  WMSOAPFault::Sender, L"Encountered unrecognized array item accessor type.", true);
            }
        }
        else
        {
            // complex type... no base type given... assume struct
            Vartype = VT_DISPATCH;
        }
    }
    long lNumDims;
    if (pParam->IsNil() == true)
    {
        // if nil array, go to WSDL for nbr dimensions
        pIWSDLClient->GetArrayDims( bstrArrayName,
                                    bstrArrayNS,
                                    &lNumDims,
                                    &bResult);
        if (bResult == VARIANT_FALSE)
        {
		    throw new WMSOAPFault(  WMSOAPFault::Sender, L"Unable to determine dimensionality of array.", true);
        }
    }
    else
    {
        // get number of dimensions from serialized array
        lNumDims = pParam->GetArrayDims().size();
    }
    // content
    SAFEARRAY *pArray = NULL;
    try
    {
        pArray = DeserializeSafeArrayEnc(   wstrEncStyle,
                                            pParam,
                                            Vartype,
                                            bstrType,
                                            bstrTypeNS,
                                            lNumDims,
                                            Msg,
                                            pIWSDLClient);
    }
    catch(WMSOAPFault *f)
    {
        SysFreeString(bstrType);
        SysFreeString(bstrTypeNS);
        throw f;
    }
    SysFreeString(bstrType);
    SysFreeString(bstrTypeNS);
    return pArray;
}

SAFEARRAY *WMSOAPDeserializer::DeserializeArrayLit( WMSOAPParamHandler *pParam,
                                                    BSTR bstrItemName,
                                                    BSTR bstrItemNS,
                                                    BSTR bstrItemType,
                                                    BSTR bstrItemTypeNS,
                                                    WMSOAPMessage& Msg,
                                                    Iwmwsdlclient2 *pIWSDLClient)
{
    VARTYPE Vartype;
    map<wstring, VARTYPE>::iterator iV;
    // is this simple (intrinsic) part?
    wstring wstrItemType(bstrItemType);
    wstring wstrItemTypeNS(bstrItemTypeNS);
    if (IsXSDType(wstrItemTypeNS) || IsSoapEncType(wstrItemTypeNS))
    {
        // look up type in map
        if ((iV = m_mapVarTypes.find(wstrItemType)) != m_mapVarTypes.end())
        {
            // built-in type
            Vartype = (*iV).second;
        }
        else
        {
            // unrecognized type
            goto badtype;
        }
    }
    else
    {
	    // complex type, see if it is array type
		BSTR bstrChildItemType, bstrChildItemTypeNS, bstrChildItemName, bstrChildItemNS;
        VARIANT_BOOL bResult;
	    pIWSDLClient->GetArrayTypeLit(	bstrItemType,
								        bstrItemTypeNS,
								        &bstrChildItemType,
								        &bstrChildItemTypeNS,
                                        &bstrChildItemName,
                                        &bstrChildItemNS,
								        &bResult);
	    if (bResult == VARIANT_TRUE)
	    {
            // this is an array type
			SysFreeString(bstrChildItemType);
			SysFreeString(bstrChildItemTypeNS);
			SysFreeString(bstrChildItemName);
			SysFreeString(bstrChildItemNS);
            Vartype = VT_VARIANT | VT_ARRAY;
        }
        else
        {
            // not an array, look for 'base' type
            BSTR bstrBaseType, bstrBaseTypeNS;
            pIWSDLClient->GetComplexBaseType(   bstrItemType,
                                                bstrItemTypeNS,
                                                &bstrBaseType,
                                                &bstrBaseTypeNS,
                                                &bResult);
            if (bResult == VARIANT_FALSE)
            {
                // type not found in schema
                goto badtype;
            }
            wstring wstrBaseType(bstrBaseType);
            wstring wstrBaseTypeNS(bstrBaseTypeNS);
            SysFreeString(bstrBaseType);
            SysFreeString(bstrBaseTypeNS);
            if (IsXSDType(wstrBaseTypeNS) || IsSoapEncType(wstrBaseTypeNS))
            {
                // is base type found in map?
                if ((iV = m_mapVarTypes.find(wstrBaseType)) != m_mapVarTypes.end())
                {
                    // yes, it's a built-in type
                    Vartype = (*iV).second;
                }
                else
                {
                    // no, unrecognized type
                    goto badtype;
                }
            }
            else
            {
                // complex type... no base type given... assume struct
                Vartype = VT_DISPATCH;
            }
        }
    }
    return DeserializeSafeArrayLit( pParam,
                                    Vartype,
                                    bstrItemName,
                                    bstrItemNS,
                                    bstrItemType,
                                    bstrItemTypeNS,
                                    Msg,
                                    pIWSDLClient);
badtype:
    throw new WMSOAPFault(  WMSOAPFault::Sender,
                            L"Encountered array item accessor with unrecognized type.",
                            true);
}

bool WMSOAPDeserializer::DeserializeComplexTypeEnc( const wstring& wstrEncStyle,
                                                    WMSOAPMessage& Msg,
                                                    Iwmwsdlclient2 *pIWSDLClient,
                                                    WMSOAPParamHandler *pRefValue,
                                                    WMSOAPParamHandler *pParamHandler,
                                                    bool bIsRef,
                                                    BSTR bstrType,
                                                    BSTR bstrTypeNS,
                                                    _variant_t& varValue)
{
    wstring wstrMsg, wstrBaseType, wstrBaseTypeNS;
    BSTR bstrBaseType, bstrBaseTypeNS;
    VARIANT_BOOL bResult;
    pIWSDLClient->GetComplexBaseType(   bstrType,
                                        bstrTypeNS,
                                        &bstrBaseType,
                                        &bstrBaseTypeNS,
                                        &bResult);
    if (bResult == VARIANT_FALSE)
    {
        throw new WMSOAPFault(  WMSOAPFault::Sender,
                                L"Encountered accessor with unrecognized type.",
                                true);
    }
    wstrBaseType = bstrBaseType;
    wstrBaseTypeNS = bstrBaseTypeNS;
    SysFreeString(bstrBaseType);
    SysFreeString(bstrBaseTypeNS);
    if (IsSoapEncType(wstrBaseTypeNS))
    {
        if (wstrBaseType == L"Array")
        {
            // SOAP-ENC:Array
            SAFEARRAY *pArray = NULL;
            // is this a multi-ref?
            if (bIsRef == true)
            {
                // transfer any SOAP-ENC:offset attribute information
                if (pParamHandler->GetOffset().empty() == false)
                {
                    pRefValue->SetArrayOffsets(pParamHandler->GetOffset());
                }
            }
            pArray = DeserializeArrayEnc(   wstrEncStyle,
                                            bstrType,
                                            bstrTypeNS,
                                            pRefValue,
                                            Msg,
                                            pIWSDLClient);
            varValue.vt = VT_VARIANT | VT_ARRAY;
            varValue.parray = pArray;
        }
        else if (wstrBaseType == L"Struct")
        {
            // SOAP-ENC:Struct
            IDispatch *pStruct = NULL;
            pStruct = DeserializeStructEnc( wstrEncStyle,
                                            bstrType,
                                            bstrTypeNS,
                                            pRefValue,
                                            Msg,
                                            pIWSDLClient);
            varValue.vt = VT_DISPATCH;
            varValue.pdispVal = pStruct;
        }
        else if (IsXSDType(wstrBaseTypeNS) || IsSoapEncType(wstrBaseTypeNS))
        {
            // base type is simple (intrinsic) part
            DeserializeBuiltInTypeEnc(  wstrEncStyle,
                                        Msg,
                                        pRefValue,
                                        wstrBaseType,
                                        varValue);
        }
        else
        {
            // unrecognized SOAP-ENC type
            throw new WMSOAPFault(  WMSOAPFault::Sender,
                                    L"Encountered accessor with unrecognized type.",
                                    true);
        }
    }
    else
    {
        // complex type... no base type given... assume struct
        IDispatch *pStruct = NULL;
        pStruct = DeserializeStructEnc( wstrEncStyle,
                                        bstrType,
                                        bstrTypeNS,
                                        pRefValue,
                                        Msg,
                                        pIWSDLClient);
        varValue.vt = VT_DISPATCH;
        varValue.pdispVal = pStruct;
    }
    return true;
}

bool WMSOAPDeserializer::DeserializeComplexTypeLit( WMSOAPMessage& Msg,
                                                    Iwmwsdlclient2 *pIWSDLClient,
                                                    WMSOAPParamHandler *pParamHandler,
                                                    BSTR bstrType,
                                                    BSTR bstrTypeNS,
                                                    _variant_t& varValue)
{
	// 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)
	{
        // this is an array type
        SAFEARRAY *pArray = NULL;
        try
        {
            pArray = DeserializeArrayLit(   pParamHandler,
                                            bstrItemName,
                                            bstrItemNS,
                                            bstrItemType,
                                            bstrItemTypeNS,
                                            Msg,
                                            pIWSDLClient);
        }
        catch (WMSOAPFault *f)
        {
		    SysFreeString(bstrItemType);
		    SysFreeString(bstrItemTypeNS);
		    SysFreeString(bstrItemName);
		    SysFreeString(bstrItemNS);
            throw f;
        }
		SysFreeString(bstrItemType);
		SysFreeString(bstrItemTypeNS);
		SysFreeString(bstrItemName);
		SysFreeString(bstrItemNS);
        varValue.vt = VT_VARIANT | VT_ARRAY;
        varValue.parray = pArray;
    }
    else
    {
        // either a struct or complex derived type
        BSTR bstrBaseType, bstrBaseTypeNS;
        VARIANT_BOOL bResult;
        pIWSDLClient->GetComplexBaseType(   bstrType,
                                            bstrTypeNS,
                                            &bstrBaseType,
                                            &bstrBaseTypeNS,
                                            &bResult);
        if (bResult == VARIANT_FALSE)
        {
            // not properly defined in WSDL file
            throw new WMSOAPFault(  WMSOAPFault::Sender,
                                    L"Encountered accessor with unrecognized type.",
                                    true);
        }
        wstring wstrBaseType(bstrBaseType);
        wstring wstrBaseTypeNS(bstrBaseTypeNS);
        SysFreeString(bstrBaseType);
        SysFreeString(bstrBaseTypeNS);
        if (IsXSDType(wstrBaseTypeNS) || IsSoapEncType(wstrBaseTypeNS))
        {
            // base type is simple (intrinsic) part
            DeserializeBuiltInTypeLit(  Msg,
                                        pParamHandler,
                                        wstrBaseType,
                                        varValue);
        }
        else
        {
            // complex type... no base type given... assume struct
            IDispatch *pStruct = NULL;
            pStruct = DeserializeStructLit( bstrType,
                                            bstrTypeNS,
                                            pParamHandler,
                                            Msg,
                                            pIWSDLClient);
            varValue.vt = VT_DISPATCH;
            varValue.pdispVal = pStruct;
        }
    }
    return true;
}

bool WMSOAPDeserializer::DeserializeBuiltInTypeEnc( const wstring& wstrEncStyle,
                                                    WMSOAPMessage& Msg,
                                                    WMSOAPParamHandler *pHandler,
                                                    const wstring& wstrType,
                                                    _variant_t& varValue)
{
    // must be character content only
    if (pHandler->GetChildren().size() > 0)
    {
        throw new WMSOAPFault(  WMSOAPFault::Sender,
                                L"Encountered improperly serialized built-in type accessor.",
                                true);
    }
    // must have nodeType="simple"
    if (pHandler->NodeTypeAsserted() == true)
    {
        if (pHandler->GetNodeType() != L"simple")
        {
            throw new WMSOAPFault(  WMSOAPFault::Sender,
                                    L"Incorrect nodeType on built-in type accessor.",
                                    true);
        }
    }
    // must not have conflicting encodingStyle
    if (pHandler->CheckEncodingStyle(wstrEncStyle) == false)
    {
        wchar_t *pwszMsg = L"Unsupported encodingStyle on built-in type accessor.";
        if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
        {
		    throw new WMSOAPFault(WMSOAPFault::Sender, pwszMsg, true);
        }
        else
        {
		    throw new WMSOAPFault(WMSOAPFault::DataEncodingUnknown, pwszMsg, true);
        }
    }
    return DeserializeBuiltInType(Msg, pHandler, wstrType, varValue);
}

bool WMSOAPDeserializer::DeserializeBuiltInTypeLit( WMSOAPMessage& Msg,
                                                    WMSOAPParamHandler *pHandler,
                                                    const wstring& wstrType,
                                                    _variant_t& varValue)
{
    return DeserializeBuiltInType(Msg, pHandler, wstrType, varValue);
}

bool WMSOAPDeserializer::DeserializeBuiltInType(WMSOAPMessage& Msg,
                                                WMSOAPParamHandler *pHandler,
                                                const wstring& wstrType,
                                                _variant_t& varValue)
{
    // look it up in VARTYPE map
    VARTYPE Vartype;
    map<wstring, VARTYPE>::iterator iV;
    if ((iV = m_mapVarTypes.find(wstrType)) != m_mapVarTypes.end())
    {
        // built-in type - namespace xsd or SOAP-ENC
        Vartype = (*iV).second;
        // is this a polymorphic type?
        if (Vartype == XT_ANYTYPE && pHandler->IsExplicitlyTyped() == true)
        {
            // yes... try to look up explicit type in VARTYPE map
            if ((iV = m_mapVarTypes.find(pHandler->GetType())) != m_mapVarTypes.end())
            {
                Vartype = (*iV).second;
            }
            else
            {
                // not found in map
                if (pHandler->GetNodeType() != L"simple")
                {
                    throw new WMSOAPFault(  WMSOAPFault::Sender,
                                            L"Encountered accessor with unrecognized data type.",
                                            true);
                }
            }
        }
        // does it need special handling?
        if (pHandler->IsNil() == true)
        {
            // nil value, set Variant to NULL type
            if (FAILED(VariantChangeType(&varValue, &varValue, 0, VT_NULL)))
            {
                goto fault;
            }
            return true;
        }
        if (Vartype < XT_ANYTYPE)
        {
            // no, assign element content
            varValue = pHandler->GetCharContent().c_str();
            // now change type as required
            if (FAILED(VariantChangeType(&varValue, &varValue, 0, Vartype)))
            {
                goto fault;
            }
        }
        else
        {
            // yes... these types don't map directly to automation types
            switch(Vartype)
            {
            case XT_ANYTYPE:
            case XT_ANYURI:
                {
                    varValue = pHandler->GetCharContent().c_str();
                }
                break;
            case XT_BOOLEAN:
                {
                    varValue = pHandler->GetCharContent().c_str();
                    if (FAILED(VariantChangeType(&varValue, &varValue, 0, VT_BOOL)))
                    {
                        goto fault;
                    }
                }
                break;
            case XT_BASE64:
                {
                    // xsd:base64Binary or SOAP-ENC:base64
                    SAFEARRAY *pArray = NULL;
                    pArray = DeserializeBase64Vector(pHandler);
                    if (pArray == NULL)
                    {
                        goto fault;
                    }
                    varValue.vt = VT_UI1 | VT_ARRAY;
                    varValue.parray = pArray;
                }
                break;
            case XT_HEXBIN:
                {
                    // xsd:hexBinary
                    SAFEARRAY *pArray = NULL;
                    pArray = DeserializeHexBinaryVector(pHandler);
                    if (pArray == NULL)
                    {
                        goto fault;
                    }
                    varValue.vt = VT_UI1 | VT_ARRAY;
                    varValue.parray = pArray;
                }
                break;
            case XT_DATETIME:
                {
                    DATE d;
                    if (DeserializeDateTime(pHandler, &d) == false)
                    {
                        goto fault;
                    }
                    varValue.vt = VT_DATE;
                    varValue.date = d;
                }
                break;
            case XT_DATE:
                {
                    DATE d;
                    if (DeserializeDate(pHandler, &d) == false)
                    {
                        goto fault;
                    }
                    varValue.vt = VT_DATE;
                    varValue.date = d;
                }
                break;
            case XT_TIME:
                {
                    DATE d;
                    if (DeserializeTime(pHandler, &d) == false)
                    {
                        goto fault;
                    }
                    varValue.vt = VT_DATE;
                    varValue.date = d;
                }
                break;
            default:
                {
                    // invalid
                    goto fault;
                }
            }
        }
    }
    else
    {
        // not found in map
        goto fault;
    }
    return true;

fault:

	wchar_t wszMsg[512];
	_snwprintf(wszMsg, 511,
        L"Encountered improperly serialized built-in type accessor: '%s'.",
            pHandler->GetLocalName().c_str());
    wszMsg[511] = 0;
    throw new WMSOAPFault(WMSOAPFault::Sender, wszMsg, true);
}

bool WMSOAPDeserializer::DeserializeDateTime(WMSOAPParamHandler *pParam, DATE *pDate)
{
    wstring wstrTemp = pParam->GetCharContent();
    // find delimiter 'T' expected format is "CCYY-MM-DDTHH:MM:SS[Z]" or "CCYY-MM-DDTHH:MM:SS[+-]HH:MM"
    wstring::size_type pos;
    pos = wstrTemp.find(wchar_t('T'));
    if (pos == wstring::npos)
    {
        // invalid
        return false;
    }
    BSTR bstrDate = SysAllocString(wstrTemp.substr(0, pos).c_str());
    BSTR bstrTime = SysAllocString(wstrTemp.substr(pos + 1, 8).c_str());

    DATE d, t;
    HRESULT hRes = VarDateFromStr(bstrDate, LOCALE_SYSTEM_DEFAULT, VAR_DATEVALUEONLY, &d);
    SysFreeString(bstrDate);
    if (FAILED(hRes))
    {
        return false;
    }
    hRes = VarDateFromStr(bstrTime, LOCALE_SYSTEM_DEFAULT, VAR_TIMEVALUEONLY, &t);
    SysFreeString(bstrTime);
    if (FAILED(hRes))
    {
        return false;
    }
    // check for time zone
    wstrTemp = wstrTemp.substr(pos + 1);
    if ((pos = wstrTemp.find(wchar_t('Z'))) == wstring::npos)
    {
        HRESULT hRes;
        BSTR bstrZone;
        bool bPositiveOffset;
        bool bTimeZoneFound = true;
        // not UTC
        if ((pos = wstrTemp.find(wchar_t('+'))) != wstring::npos)
        {
            // positive time zone offset
            bPositiveOffset = true;
        }
        else if ((pos = wstrTemp.find(wchar_t('-'))) != wstring::npos)
        {
            // negative time zone offset
            bPositiveOffset = false;
        }
        else
        {
            bTimeZoneFound = false;
        }
        if (bTimeZoneFound == true)
        {
            // normalize to UTC
            wstrTemp = wstrTemp.substr(pos + 1, 5);
            wstrTemp += L":00";
            DATE z;
            bstrZone = SysAllocString(wstrTemp.c_str());
            hRes = VarDateFromStr(bstrZone, LANG_USER_DEFAULT, VAR_TIMEVALUEONLY, &z);
            SysFreeString(bstrZone);
            if (FAILED(hRes))
            {
                return false;
            }
            if (bPositiveOffset == false)
            {
                t = t + z;
                if (t > 1.0)
                {
                    d = d + 1.0;
                    t = t - 1.0;
                }
            }
            else
            {
                t = t - z;
                if (t < 1.0)
                {
                    d = d - 1.0;
                    t = t + 1.0;
                }
            }
        }
    }
    *pDate = d + t;
    return true;
}

bool WMSOAPDeserializer::DeserializeTime(WMSOAPParamHandler *pParam, DATE *pDate)
{
    wstring wstrTemp = pParam->GetCharContent();
    // expected format is "HH:MM:SS[Z]" or "HH:MM:SS[+-]HH:MM"
    BSTR bstrTime = SysAllocString(wstrTemp.substr(0, 8).c_str());

    DATE t;
    HRESULT hRes = VarDateFromStr(bstrTime, LANG_USER_DEFAULT, VAR_TIMEVALUEONLY, &t);
    SysFreeString(bstrTime);
    if (FAILED(hRes))
    {
        return false;
    }
    // check for time zone
    wstring::size_type pos;
    if ((pos = wstrTemp.find(wchar_t('Z'))) == wstring::npos)
    {
        HRESULT hRes;
        BSTR bstrZone;
        bool bPositiveOffset;
        bool bTimeZoneFound = true;
        // not UTC
        if ((pos = wstrTemp.find(wchar_t('+'))) != wstring::npos)
        {
            // positive time zone offset
            bPositiveOffset = true;
        }
        else if ((pos = wstrTemp.find(wchar_t('-'))) != wstring::npos)
        {
            // negative time zone offset
            bPositiveOffset = false;
        }
        else
        {
            bTimeZoneFound = false;
        }
        if (bTimeZoneFound = true)
        {
            // normalize to UTC
            wstrTemp = wstrTemp.substr(pos + 1, 5);
            wstrTemp += L":00";
            DATE z;
            bstrZone = SysAllocString(wstrTemp.c_str());
            hRes = VarDateFromStr(bstrZone, LANG_USER_DEFAULT, VAR_TIMEVALUEONLY, &z);
            SysFreeString(bstrZone);
            if (FAILED(hRes))
            {
                return false;
            }
            if (bPositiveOffset == false)
            {
                t = t + z;
                if (t > 1.0)
                {
                    t = t - 1.0;
                }
            }
            else
            {
                t = t - z;
                if (t < 1.0)
                {
                    t = t + 1.0;
                }
            }
        }
    }
    *pDate = t;
    return true;
}

bool WMSOAPDeserializer::DeserializeDate(WMSOAPParamHandler *pParam, DATE *pDate)
{
    wstring wstrTemp = pParam->GetCharContent();
    // expected format "CCYY-MM-DD[Z]" or "CCYY-MM-DD[+-]HH:MM"
    BSTR bstrDate = SysAllocString(wstrTemp.substr(0, 10).c_str());

    DATE d;
    HRESULT hRes = VarDateFromStr(bstrDate, LANG_USER_DEFAULT, VAR_DATEVALUEONLY, &d);
    SysFreeString(bstrDate);
    if (FAILED(hRes))
    {
        return false;
    }
    *pDate = d;
    return true;
}

bool WMSOAPDeserializer::GetDIMEAttachment( WMSOAPMessage& Msg,
                                            Iwmwsdlclient2 *pIWSDLClient,
                                            WMSOAPParamHandler *pParamHandler,
                                            const wstring& wstrParamType,
                                            const wstring& wstrParamTypeNS,
                                            _variant_t& varValue)
{
    // get attachment whose id matches value of href for parameter accessor
    IUnknown *pIUnknown;
    long lTNF;
    wstring wstrType;
    wstring wstrId = pParamHandler->GetHref();
    bool bResult;
    if (Msg.IsRequestPhase() == true)
    {
        bResult = Msg.GetDIMEAttachmentReq( wstrId,
                                            wstrType,
                                            lTNF,
                                            &pIUnknown);
    }
    else
    {
        bResult = Msg.GetDIMEAttachmentResp(wstrId,
                                            wstrType,
                                            lTNF,
                                            &pIUnknown);
    }
    if (bResult == true)
    {
        pIUnknown->AddRef();
        varValue.punkVal = pIUnknown;
        varValue.vt = VT_UNKNOWN;
    }
    return bResult;
}
