2151 lines
57 KiB
C++
2151 lines
57 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1999.
|
|
//
|
|
// File: ncxml.cpp
|
|
//
|
|
// Contents: helper functions for doing remarkably simple things
|
|
// with the XML DOM.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
#include "pch.h"
|
|
#pragma hdrstop
|
|
|
|
#include <oleauto.h> // for SysFreeString()
|
|
|
|
#include "ncbase.h"
|
|
#include "ncdebug.h"
|
|
|
|
#include "ncxml.h"
|
|
#include "ncinet2.h" // HrCombineUrl, HrCopyAndValidateUrl
|
|
|
|
|
|
struct DTNAME
|
|
{
|
|
LPCWSTR m_pszType;
|
|
SST_DATA_TYPE m_sdt;
|
|
};
|
|
|
|
|
|
// Lookup table, sorted by string
|
|
// Note: if you add strings here, you also have to add a
|
|
// corresponding entry to SST_DATA_TYPE
|
|
//
|
|
static CONST DTNAME g_rgdtTypeStrings[] =
|
|
{
|
|
{L"bin.base64", SDT_BIN_BASE64}, // base64 as defined in the MIME IETF spec
|
|
{L"bin.hex", SDT_BIN_HEX}, // Hexadecimal digits representing octets VT_ARRAY safearray or stream
|
|
{L"boolean", SDT_BOOLEAN}, // boolean, "1" or "0" VT_BOOL int
|
|
{L"char", SDT_CHAR}, // char, string VT_UI2 wchar
|
|
{L"date", SDT_DATE_ISO8601}, // date.iso8601, A date in ISO 8601 format. (no time) VT_DATE long
|
|
{L"dateTime", SDT_DATETIME_ISO8601}, // dateTime.iso8601, A date in ISO 8601 format, with optional time and no optional zone. Fractional seconds may be as precise as nanoseconds. VT_DATE long
|
|
{L"dateTime.tz", SDT_DATETIME_ISO8601TZ},// dateTime.iso8601tz, A date in ISO 8601 format, with optional time and optional zone. Fractional seconds may be as precise as nanoseconds. VT_DATE long
|
|
{L"fixed.14.4", SDT_FIXED_14_4}, // fixed.14.4, Same as "number" but no more than 14 digits to the left of the decimal point, and no more than 4 to the right. VT_CY large_integer
|
|
{L"float", SDT_FLOAT}, // float, Same as for "number." VT_R8 double
|
|
{L"i1", SDT_I1}, // i1, A number, with optional sign, no fractions, no exponent. VT_I1 char
|
|
{L"i2", SDT_I2}, // i2, " VT_I2 short
|
|
{L"i4", SDT_I4}, // i4, " VT_I4 long
|
|
{L"int", SDT_INT}, // int, A number, with optional sign, no fractions, no exponent. VT_I4 long
|
|
{L"number", SDT_NUMBER}, // number, A number, with no limit on digits, may potentially have a leading sign, fractional digits, and optionally an exponent. Punctuation as in US English. VT_R8 double
|
|
{L"r4", SDT_R4}, // r4, Same as "number." VT_FLOAT float
|
|
{L"r8", SDT_R8}, // r8, " VT_DOUBLE double
|
|
{L"string", SDT_STRING}, // string, pcdata VT_BSTR BSTR
|
|
{L"time", SDT_TIME_ISO8601}, // time.iso8601, A time in ISO 8601 format, with no date and no time zone. VT_DATE long
|
|
{L"time.tz", SDT_TIME_ISO8601TZ}, // time.iso8601.tz, A time in ISO 8601 format, with no date but optional time zone. VT_DATE. long
|
|
{L"ui1", SDT_UI1}, // ui1, A number, unsigned, no fractions, no exponent. VT_UI1 unsigned char
|
|
{L"ui2", SDT_UI2}, // ui2, " VT_UI2 unsigned short
|
|
{L"ui4", SDT_UI4}, // ui4, " VT_UI4 unsigned long
|
|
{L"uri", SDT_URI}, // uri, Universal Resource Identifier VT_BSTR BSTR
|
|
{L"uuid", SDT_UUID}, // uuid, Hexadecimal digits representing octets, optional embedded hyphens which should be ignored. VT_BSTR GUID
|
|
//
|
|
// note(cmr): ADD NEW VALUES IN ALPHABETICAL ORDER
|
|
//
|
|
};
|
|
|
|
// NOTE: the order of elements in this array must correspond to the order of
|
|
// elements in the SST_DATA_TYPE enum.
|
|
static CONST VARTYPE g_rgvtTypes[] =
|
|
{
|
|
VT_BSTR,
|
|
VT_BSTR,
|
|
VT_I4,
|
|
VT_CY,
|
|
VT_BOOL,
|
|
VT_DATE,
|
|
VT_DATE,
|
|
VT_DATE,
|
|
VT_DATE,
|
|
VT_DATE,
|
|
VT_I1,
|
|
VT_I2,
|
|
VT_I4,
|
|
VT_UI1,
|
|
VT_UI2,
|
|
VT_UI4,
|
|
VT_R4,
|
|
VT_R8,
|
|
VT_R8,
|
|
VT_BSTR,
|
|
VT_ARRAY,
|
|
VT_ARRAY,
|
|
VT_UI2,
|
|
VT_BSTR,
|
|
//
|
|
// note(cmr): ADD NEW VALUES IMMEDIATELY BEFORE THIS COMMENT.
|
|
// If adding new values, see comment above.
|
|
//
|
|
VT_EMPTY
|
|
};
|
|
|
|
|
|
/*
|
|
* Function: GetStringFromType()
|
|
*
|
|
* Purpose: Returns an xml-data type string for the specified
|
|
* SDT_DATA_TYPE value.
|
|
*
|
|
* Arguments:
|
|
* sdt [in] The data type whose descriptive string is desired.
|
|
* This must be a valid SST_DATA_TYPE value (less
|
|
* than SDT_INVALID)
|
|
*
|
|
* Returns:
|
|
* A pointer to a constant string id representing the type. This string
|
|
* should not be modified or freed.
|
|
* This method will never return NULL.
|
|
*
|
|
*/
|
|
LPCWSTR
|
|
GetStringFromType(CONST SST_DATA_TYPE sdt)
|
|
{
|
|
// there must be an entry in g_rgdtTypeStrings for every real
|
|
// value of SST_DATA_TYPE
|
|
//
|
|
Assert(SDT_INVALID == celems(g_rgdtTypeStrings));
|
|
Assert(sdt < SDT_INVALID);
|
|
|
|
// since comparing SDT_s is somewhat cheaper than comparing whole strings,
|
|
// we just walk through the whole list sequentually
|
|
//
|
|
|
|
CONST INT nSize = celems(g_rgdtTypeStrings);
|
|
LPCWSTR pszResult;
|
|
INT i;
|
|
|
|
pszResult = NULL;
|
|
i = 0;
|
|
for ( ; i < nSize; ++i)
|
|
{
|
|
SST_DATA_TYPE sdtCurrent;
|
|
|
|
sdtCurrent = g_rgdtTypeStrings[i].m_sdt;
|
|
if (sdt == sdtCurrent)
|
|
{
|
|
// we have a match
|
|
pszResult = g_rgdtTypeStrings[i].m_pszType;
|
|
break;
|
|
}
|
|
}
|
|
|
|
AssertSz(pszResult, "GetStringFromType: "
|
|
"sdt not found in g_rgdtTypeStrings!");
|
|
|
|
return pszResult;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: GetTypeFromString()
|
|
*
|
|
* Purpose: Returns the appropriate SDT_DATA_TYPE value for the given
|
|
* xml-data type string.
|
|
*
|
|
* Arguments:
|
|
* pszTypeString [in] The data type identifier whose SDT_DATA_TYPE value
|
|
* is desired.
|
|
*
|
|
* Returns:
|
|
* If the string is found, it returns the appropriate value of the
|
|
* SST_DATA_TYPE enumeration.
|
|
* If the string is not found, it returns SDT_INVALID.
|
|
*
|
|
* Notes:
|
|
* The source string is compared to known strings in a case-insensitive
|
|
* comparison.
|
|
*
|
|
*/
|
|
SST_DATA_TYPE
|
|
GetTypeFromString(LPCWSTR pszTypeString)
|
|
{
|
|
// there must be an entry in g_rgdtTypeStrings for every real
|
|
// value of SST_DATA_TYPE
|
|
//
|
|
Assert(SDT_INVALID == celems(g_rgdtTypeStrings));
|
|
|
|
SST_DATA_TYPE sdtResult;
|
|
|
|
sdtResult = SDT_INVALID;
|
|
|
|
{
|
|
// now search for the string in the list, using a
|
|
// standard binary search
|
|
//
|
|
INT nLow;
|
|
INT nMid;
|
|
INT nHigh;
|
|
|
|
nLow = 0;
|
|
nHigh = celems(g_rgdtTypeStrings) - 1;
|
|
|
|
while (TRUE)
|
|
{
|
|
if (nLow > nHigh)
|
|
{
|
|
// not found
|
|
//
|
|
break;
|
|
}
|
|
|
|
nMid = (nLow + nHigh) / 2;
|
|
|
|
{
|
|
LPCWSTR pszCurrent;
|
|
int result;
|
|
|
|
pszCurrent = g_rgdtTypeStrings[nMid].m_pszType;
|
|
|
|
result = _wcsicmp(pszTypeString, pszCurrent);
|
|
if (result < 0)
|
|
{
|
|
nHigh = nMid - 1;
|
|
}
|
|
else if (result > 0)
|
|
{
|
|
nLow = nMid + 1;
|
|
}
|
|
else
|
|
{
|
|
// found
|
|
//
|
|
sdtResult = g_rgdtTypeStrings[nMid].m_sdt;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return sdtResult;
|
|
}
|
|
|
|
VARTYPE
|
|
GetVarTypeFromString(LPCWSTR pszTypeString)
|
|
{
|
|
SST_DATA_TYPE sdt = SDT_INVALID;
|
|
|
|
sdt = GetTypeFromString(pszTypeString);
|
|
|
|
return g_rgvtTypes[sdt];
|
|
}
|
|
|
|
|
|
// not an exported function, but one that we use internally...
|
|
HRESULT
|
|
HrGetChildElement(IXMLDOMNode * pxdn,
|
|
LPCWSTR pszNodeName,
|
|
IXMLDOMNode ** ppxdn);
|
|
|
|
|
|
CONST WCHAR pszTextType [] = L"string";
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: HrGetTypedValueFromElement
|
|
//
|
|
// Purpose: Given an IXMLDOMNode that should be of type
|
|
// dt:string, returns a new BSTR containing that
|
|
// string.
|
|
//
|
|
// Arguments:
|
|
// pxdn IXMLDOMNode to extract the string from.
|
|
// It is intended that this node be of type
|
|
// NODE_ELEMENT.
|
|
//
|
|
// pszDataType The type of the data encoded in the element.
|
|
//
|
|
// pvarOut Address of a VARIANT that will obtain the
|
|
// data value. This must be freed when
|
|
// no longer needed.
|
|
//
|
|
// Returns:
|
|
// S_OK if *pvarOut contains the data of the desired type
|
|
//
|
|
// Notes:
|
|
//
|
|
HRESULT
|
|
HrGetTypedValueFromElement(IXMLDOMNode * pxdn,
|
|
CONST LPCWSTR pszDataType,
|
|
VARIANT * pvarOut)
|
|
{
|
|
Assert(pxdn);
|
|
Assert(pszDataType);
|
|
Assert(pvarOut);
|
|
|
|
HRESULT hr;
|
|
BSTR bstrDataType;
|
|
|
|
bstrDataType = ::SysAllocString(pszDataType);
|
|
if (bstrDataType)
|
|
{
|
|
hr = pxdn->put_dataType(bstrDataType);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pxdn->get_nodeTypedValue(pvarOut);
|
|
if (FAILED(hr))
|
|
{
|
|
TraceError("HrGetTypedValueFromElement: "
|
|
"get_nodeTypedValue()", hr);
|
|
|
|
// clear pvarOut
|
|
::VariantInit(pvarOut);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TraceError("HrGetTypedValueFromElement: "
|
|
"put_dataType()", hr);
|
|
}
|
|
|
|
::SysFreeString(bstrDataType);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
TraceError("HrGetTypedValueFromElement: "
|
|
"SysAllocString()", hr);
|
|
}
|
|
|
|
TraceError("HrGetTypedValueFromElement", hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: HrGetTypedValueFromChildElement
|
|
//
|
|
// Purpose: Given an IXMLDOMElement, finds its child element
|
|
// of the given name, and extracts the data contained
|
|
// in that child element, decoding it from the specified
|
|
// format.
|
|
//
|
|
// Arguments:
|
|
// pxdn The element which should contain the specified
|
|
// children
|
|
//
|
|
// arypszTokens A serial list of child element names that uniquely
|
|
// describe the desired element.
|
|
//
|
|
// cTokens The number of element names contained in
|
|
// arypszTokens.
|
|
//
|
|
// pszDataType The type of the data encoded in the element.
|
|
//
|
|
// pvarOut Address of a VARIANT that will obtain the
|
|
// data value. This must be freed when
|
|
// no longer needed.
|
|
//
|
|
// Returns:
|
|
// S_OK if *pbstrOut has been set to a new BSTR
|
|
// S_FALSE if the specified element did not exist.
|
|
// in this case, *pbstrOut is set to NULL
|
|
//
|
|
// Notes:
|
|
// for example, if the document looked like this:
|
|
// <foo><bar>text</bar></foo>
|
|
// and pxdn referred to <foo>, arypszTokens = [ "bar" ]
|
|
// cTokens = 1, and sdtType = "string", this would
|
|
// return "text".
|
|
// See the definition of HrGetNestedChildElement() for
|
|
// further explination.
|
|
//
|
|
HRESULT
|
|
HrGetTypedValueFromChildElement(IXMLDOMNode * pxdn,
|
|
CONST LPCWSTR * arypszTokens,
|
|
CONST ULONG cTokens,
|
|
CONST LPCWSTR pszDataType,
|
|
VARIANT * pvarOut)
|
|
{
|
|
Assert(pxdn);
|
|
Assert(arypszTokens);
|
|
Assert(cTokens);
|
|
Assert(pszDataType);
|
|
Assert(pvarOut);
|
|
|
|
HRESULT hr;
|
|
IXMLDOMNode * pxdnTemp;
|
|
|
|
pxdnTemp = NULL;
|
|
|
|
hr = HrGetNestedChildElement(pxdn, arypszTokens, cTokens, &pxdnTemp);
|
|
if (FAILED(hr))
|
|
{
|
|
pxdnTemp = NULL;
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
Assert(pxdn);
|
|
|
|
hr = HrGetTypedValueFromElement(pxdnTemp, pszDataType, pvarOut);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
// hr is S_FALSE if bstrResult is NULL, or S_OK if
|
|
// pvarOut has been retrieved
|
|
|
|
Cleanup:
|
|
SAFE_RELEASE(pxdnTemp);
|
|
|
|
TraceErrorOptional("HrGetTypedValueFromChildElement", hr, (S_FALSE == hr));
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: HrGetTextValueFromElement
|
|
//
|
|
// Purpose: Given an IXMLDOMNode that should be of type
|
|
// dt:string, returns a new BSTR containing that
|
|
// string.
|
|
//
|
|
// Arguments:
|
|
// pxdn IXMLDOMNode to extract the string from.
|
|
// It is intended that this node be of type
|
|
// NODE_ELEMENT.
|
|
//
|
|
// pbstrOut Address of a BSTR that will obtain the
|
|
// text value. This must be freed when
|
|
// no longer needed.
|
|
//
|
|
// Returns:
|
|
// S_OK if *pbstrOut has been set to a new BSTR
|
|
//
|
|
// Notes:
|
|
// This function should return an error if the element
|
|
// contains things other than NODE_TEXT children.
|
|
//
|
|
HRESULT
|
|
HrGetTextValueFromElement(IXMLDOMNode * pxdn,
|
|
BSTR * pbstrOut)
|
|
{
|
|
Assert(pxdn);
|
|
Assert(pbstrOut);
|
|
|
|
HRESULT hr;
|
|
VARIANT varOut;
|
|
|
|
*pbstrOut = NULL;
|
|
|
|
hr = HrGetTypedValueFromElement(pxdn,
|
|
pszTextType,
|
|
&varOut);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
Assert(VT_BSTR == V_VT(&varOut));
|
|
|
|
*pbstrOut = V_BSTR(&varOut);
|
|
|
|
// note: Don't clear varOut, since it's the returned BSTR
|
|
}
|
|
|
|
TraceErrorOptional("HrGetTextValueFromElement", hr, (S_FALSE == hr));
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: HrGetTextValueFromChildElement
|
|
//
|
|
// Purpose: Given an IXMLDOMElement, finds its child element
|
|
// of the given name, and extracts the dt:type="string"
|
|
// data contained in that child element.
|
|
//
|
|
// Arguments:
|
|
// pxdn The element which should contain the specified
|
|
// children
|
|
//
|
|
// arypszTokens A serial list of child element names that uniquely
|
|
// describe the desired element.
|
|
//
|
|
// cTokens The number of element names contained in
|
|
// arypszTokens.
|
|
//
|
|
// pbstrOut Address of a BSTR that will obtain the
|
|
// text value. This must be freed when
|
|
// no longer needed.
|
|
//
|
|
// Returns:
|
|
// S_OK if *pbstrOut has been set to a new BSTR
|
|
// S_FALSE if the specified element did not exist.
|
|
// in this case, *pbstrOut is set to NULL
|
|
//
|
|
// Notes:
|
|
// for example, if the document looked like this:
|
|
// <foo><bar>text</bar></foo>
|
|
// and pxdn referred to <foo>, arypszTokens = [ "bar" ]
|
|
// and cTokens = 1, this would return "text".
|
|
// See the definition of HrGetNestedChildElement() for
|
|
// further explination.
|
|
//
|
|
HRESULT
|
|
HrGetTextValueFromChildElement(IXMLDOMNode * pxdn,
|
|
const LPCWSTR * arypszTokens,
|
|
const ULONG cTokens,
|
|
BSTR * pbstrOut)
|
|
{
|
|
HRESULT hr;
|
|
VARIANT varOut;
|
|
|
|
*pbstrOut = NULL;
|
|
|
|
hr = HrGetTypedValueFromChildElement(pxdn,
|
|
arypszTokens,
|
|
cTokens,
|
|
pszTextType,
|
|
&varOut);
|
|
if (S_OK == hr)
|
|
{
|
|
Assert(VT_BSTR == V_VT(&varOut));
|
|
|
|
*pbstrOut = V_BSTR(&varOut);
|
|
|
|
// note: Don't clear varOut, since it's the returned BSTR
|
|
}
|
|
|
|
TraceErrorOptional("HrGetTextValueFromChildElement", hr, (S_FALSE == hr));
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: FIsThisTheNodeName
|
|
//
|
|
// Purpose: Returns TRUE if the tag name of the specified XML DOM
|
|
// element is the specified string.
|
|
//
|
|
// Arguments:
|
|
// pxdn Node whose name is being tested
|
|
//
|
|
// pszNodeName Proposed name of the node
|
|
//
|
|
// Returns:
|
|
// TRUE if the node name matches, FALSE if not
|
|
//
|
|
// Notes:
|
|
// for the xml node declared as
|
|
// <ab:foo xmlns:ab="urn:...">...</ab:foo>
|
|
// IsThisTheNodeName() will return TRUE only when
|
|
// pszNodeName == T"foo";
|
|
BOOL
|
|
FIsThisTheNodeName(IXMLDOMNode * pxdn,
|
|
LPCWSTR pszNodeName)
|
|
{
|
|
Assert(pxdn);
|
|
Assert(pszNodeName);
|
|
|
|
HRESULT hr;
|
|
BSTR bstrNodeName;
|
|
BOOL fResult;
|
|
int result;
|
|
|
|
bstrNodeName = NULL;
|
|
|
|
hr = pxdn->get_baseName(&bstrNodeName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Assert(bstrNodeName);
|
|
|
|
result = wcscmp(bstrNodeName, pszNodeName);
|
|
|
|
::SysFreeString(bstrNodeName);
|
|
|
|
return (result == 0) ? TRUE : FALSE;
|
|
}
|
|
|
|
TraceError("OBJ: FIsThisTheNodeName - get_baseName", hr);
|
|
Assert(!bstrNodeName);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: FIsThisTheNodeNameWithNamespace
|
|
//
|
|
// Purpose: Returns TRUE if the element name of the specified XML DOM
|
|
// element is the specified string and is in a given namespace.
|
|
//
|
|
// Arguments:
|
|
// pxdn Node whose name is being tested
|
|
// pszNodeName Proposed base name of the node
|
|
// pszNamespaceURI Proposed namespace URI of the node
|
|
//
|
|
// Returns:
|
|
// TRUE if the node name matches, FALSE if not
|
|
//
|
|
// Notes:
|
|
// for the xml node declared as
|
|
// <ab:foo xmlns:ab="urn:...">...</ab:foo>
|
|
// FIsThisTheNodeNameWithNamespace() will return TRUE only when
|
|
// pszNodeName == L"foo" and pszNamespaceURI == L"urn:..."
|
|
BOOL
|
|
FIsThisTheNodeNameWithNamespace(IXMLDOMNode * pxdn,
|
|
LPCWSTR pszNodeName,
|
|
LPCWSTR pszNamespaceURI)
|
|
{
|
|
Assert(pxdn);
|
|
Assert(pszNodeName);
|
|
Assert(pszNamespaceURI);
|
|
|
|
HRESULT hr = S_OK;;
|
|
BSTR bstrNodeName = NULL;
|
|
BSTR bstrNamespaceURI = NULL;
|
|
BOOL fResult = FALSE;
|
|
|
|
hr = pxdn->get_baseName(&bstrNodeName);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (bstrNodeName)
|
|
{
|
|
hr = pxdn->get_namespaceURI(&bstrNamespaceURI);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (bstrNamespaceURI)
|
|
{
|
|
if ((lstrcmpW(bstrNodeName, pszNodeName) == 0) &&
|
|
(lstrcmpW(bstrNamespaceURI, pszNamespaceURI) == 0))
|
|
{
|
|
fResult = TRUE;
|
|
}
|
|
|
|
::SysFreeString(bstrNamespaceURI);
|
|
}
|
|
}
|
|
|
|
::SysFreeString(bstrNodeName);
|
|
}
|
|
}
|
|
|
|
TraceError("FIsThisTheNodeNameWithNamespace(): "
|
|
"Exiting", hr);
|
|
|
|
return fResult;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: FIsThisTheNodeTextValue
|
|
//
|
|
// Purpose: Determines whether the text value of a node matches a specified
|
|
// value
|
|
//
|
|
// Arguments:
|
|
// pxdn [in] The node to check
|
|
// cszTextValue [in] The value to compare against
|
|
//
|
|
// Returns:
|
|
// TRUE if the node's text value matches the value specified in cszTextValue
|
|
// FALSE otherwise
|
|
//
|
|
// Author: spather 2000/11/2
|
|
//
|
|
// Notes:
|
|
//
|
|
BOOL
|
|
FIsThisTheNodeTextValue(
|
|
IN IXMLDOMNode * pxdn,
|
|
IN LPCWSTR cszTextValue)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fResult = FALSE;
|
|
BSTR bstrNodeText = NULL;
|
|
|
|
Assert(pxdn);
|
|
Assert(cszTextValue);
|
|
|
|
hr = pxdn->get_text(&bstrNodeText);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Assert(bstrNodeText);
|
|
|
|
if (0 == lstrcmpW(bstrNodeText, cszTextValue))
|
|
{
|
|
fResult = TRUE;
|
|
}
|
|
SysFreeString(bstrNodeText);
|
|
}
|
|
else
|
|
{
|
|
TraceError("FIsThisTheNodeTextValue(): "
|
|
"Failed to get node text",
|
|
hr);
|
|
}
|
|
|
|
TraceError("FIsThisTheNodeTextValue(): "
|
|
"Exiting",
|
|
hr);
|
|
|
|
return fResult;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: FAreNodeValuesEqual
|
|
//
|
|
// Purpose: Compares the text values of two DOM nodes and, if they are
|
|
// equal, returns TRUE.
|
|
//
|
|
// Arguments:
|
|
// pxdn1 [in] The first node
|
|
// pxdn2 [in] The second node
|
|
//
|
|
// Returns:
|
|
// TRUE if the nodes' text values are equal, false otherwise.
|
|
//
|
|
// Author: spather 2000/11/2
|
|
//
|
|
// Notes:
|
|
//
|
|
BOOL
|
|
FAreNodeValuesEqual(
|
|
IN IXMLDOMNode * pxdn1,
|
|
IN IXMLDOMNode * pxdn2)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fResult = FALSE;
|
|
BSTR bstrNode1Text = NULL;
|
|
BSTR bstrNode2Text = NULL;
|
|
|
|
hr = pxdn1->get_text(&bstrNode1Text);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Assert(bstrNode1Text);
|
|
|
|
hr = pxdn2->get_text(&bstrNode2Text);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Assert(bstrNode2Text);
|
|
}
|
|
else
|
|
{
|
|
TraceError("FAreNodeValuesEqual(): "
|
|
"Failed to get node 2 text",
|
|
hr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TraceError("FAreNodeValuesEqual(): "
|
|
"Failed to get node 1 text",
|
|
hr);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (0 == lstrcmpW(bstrNode1Text, bstrNode2Text))
|
|
{
|
|
fResult = TRUE;
|
|
}
|
|
}
|
|
|
|
if (bstrNode1Text)
|
|
{
|
|
SysFreeString(bstrNode1Text);
|
|
}
|
|
|
|
if (bstrNode2Text)
|
|
{
|
|
SysFreeString(bstrNode2Text);
|
|
}
|
|
|
|
TraceError("FAreNodeValuesEqual(): "
|
|
"Exiting",
|
|
hr);
|
|
|
|
return fResult;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: HrAreThereDuplicatesInChildNodeTextValues
|
|
//
|
|
// Purpose: Starting from a specified parent node, this function
|
|
// examines all child nodes that have a given name, and
|
|
// checks their text values to see whether duplicates
|
|
// exist (i.e. whether two or more of these child nodes
|
|
// have the same text value).
|
|
//
|
|
// Arguments:
|
|
// pxdn [in] The parent node
|
|
// cszXSLPattern [in] XSL Pattern identifying the name of the child
|
|
// nodes to examine
|
|
// fCaseSensitive [in] Indicates whether the text values should be
|
|
// examined with a case-sensitive or case-
|
|
// insensitive comparison (if TRUE, compare will
|
|
// be case-sensitive)
|
|
// pfDuplicatesExist [out] Receives a boolean value indicating whether or
|
|
// not there were duplicates in the child node
|
|
// values. (TRUE if there were duplicates
|
|
//
|
|
// Returns:
|
|
// If the function succeeds, the return value is S_OK. Otherwise, the
|
|
// function returns one of the COM error codes defined in WinError.h.
|
|
//
|
|
// Author: spather 2000/11/5
|
|
//
|
|
// Notes:
|
|
// This function uses a very simple O(n^2) algorithm. Do not use it to
|
|
// check nodes that have very many (> 100) children.
|
|
|
|
HRESULT
|
|
HrAreThereDuplicatesInChildNodeTextValues(
|
|
IN IXMLDOMNode * pxdn,
|
|
IN LPCWSTR cszXSLPattern,
|
|
IN BOOL fCaseSensitive,
|
|
OUT BOOL * pfDuplicatesExist)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fDuplicatesExist = FALSE;
|
|
LONG lNumNodes = 0;
|
|
BSTR * rgbstrValues = NULL;
|
|
BSTR bstrXSLPattern;
|
|
IXMLDOMNodeList * pxdnlNodes = NULL;
|
|
|
|
Assert(pxdn);
|
|
Assert(cszXSLPattern);
|
|
Assert(pfDuplicatesExist);
|
|
|
|
// Get the child nodes that match the XSL pattern.
|
|
|
|
bstrXSLPattern = SysAllocString(cszXSLPattern);
|
|
|
|
if (bstrXSLPattern)
|
|
{
|
|
hr = pxdn->selectNodes(bstrXSLPattern, &pxdnlNodes);
|
|
|
|
SysFreeString(bstrXSLPattern);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
|
|
"Failed to allocate BSTR for XSL pattern",
|
|
hr);
|
|
}
|
|
|
|
// Construct an array of the child nodes' text values.
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Assert(pxdnlNodes);
|
|
|
|
hr = pxdnlNodes->get_length(&lNumNodes);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (lNumNodes > 0)
|
|
{
|
|
rgbstrValues = new BSTR[lNumNodes];
|
|
if (rgbstrValues)
|
|
{
|
|
ZeroMemory(rgbstrValues, lNumNodes * sizeof(BSTR));
|
|
|
|
for (LONG i = 0; SUCCEEDED(hr) && (i < lNumNodes); i++)
|
|
{
|
|
IXMLDOMNode * pxdnChild = NULL;
|
|
|
|
hr = pxdnlNodes->get_item(i, &pxdnChild);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Assert(pxdnChild);
|
|
|
|
hr = pxdnChild->get_text(&rgbstrValues[i]);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
|
|
"Failed to get node text value",
|
|
hr);
|
|
}
|
|
pxdnChild->Release();
|
|
}
|
|
else
|
|
{
|
|
TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
|
|
"Failed to get list item",
|
|
hr);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
|
|
"Failed to allocate text value array",
|
|
hr);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
pxdnlNodes->Release();
|
|
}
|
|
else
|
|
{
|
|
TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
|
|
"Failed to get nodes matching XSL pattern",
|
|
hr);
|
|
}
|
|
|
|
// Check the array for duplicates.
|
|
|
|
if (SUCCEEDED(hr) && (lNumNodes > 0))
|
|
{
|
|
Assert(rgbstrValues);
|
|
|
|
for (LONG i = 0; i < lNumNodes; i++)
|
|
{
|
|
for (LONG j = i+1; j < lNumNodes; j++)
|
|
{
|
|
if (fCaseSensitive)
|
|
{
|
|
if (0 == lstrcmpW(rgbstrValues[i], rgbstrValues[j]))
|
|
{
|
|
fDuplicatesExist = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (0 == lstrcmpiW(rgbstrValues[i], rgbstrValues[j]))
|
|
{
|
|
fDuplicatesExist = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clean up the array.
|
|
|
|
if (rgbstrValues)
|
|
{
|
|
for (LONG i = 0; i < lNumNodes; i++)
|
|
{
|
|
if (rgbstrValues[i])
|
|
{
|
|
SysFreeString(rgbstrValues[i]);
|
|
rgbstrValues[i] = NULL;
|
|
}
|
|
}
|
|
delete [] rgbstrValues;
|
|
rgbstrValues = NULL;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*pfDuplicatesExist = fDuplicatesExist;
|
|
}
|
|
|
|
TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
|
|
"Exiting",
|
|
hr);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
HrGetFirstChildElement(IXMLDOMNode * pxdn,
|
|
LPCWSTR pszNodeName,
|
|
IXMLDOMNode ** ppxdn)
|
|
{
|
|
Assert(pxdn);
|
|
Assert(pszNodeName);
|
|
Assert(ppxdn);
|
|
|
|
HRESULT hr;
|
|
IXMLDOMNode * pxdnChild;
|
|
|
|
pxdnChild = NULL;
|
|
|
|
hr = pxdn->get_firstChild(&pxdnChild);
|
|
if (FAILED(hr))
|
|
{
|
|
TraceError("HrGetFirstChildElement - get_firstChild", hr);
|
|
|
|
pxdnChild = NULL;
|
|
|
|
goto Cleanup;
|
|
}
|
|
// maybe there is a child, maybe not.
|
|
|
|
hr = HrGetChildElement(pxdnChild, pszNodeName, ppxdn);
|
|
|
|
Cleanup:
|
|
SAFE_RELEASE(pxdnChild);
|
|
|
|
TraceErrorOptional("HrGetFirstChildElement", hr, (S_FALSE == hr));
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
HrGetNextChildElement(IXMLDOMNode * pxdnLastChild,
|
|
LPCWSTR pszNodeName,
|
|
IXMLDOMNode ** ppxdn)
|
|
{
|
|
Assert(pxdnLastChild);
|
|
Assert(pszNodeName);
|
|
Assert(ppxdn);
|
|
|
|
HRESULT hr;
|
|
IXMLDOMNode * pxdnChild;
|
|
IXMLDOMNode * pxdnResult;
|
|
|
|
pxdnChild = NULL;
|
|
pxdnResult = NULL;
|
|
|
|
// we're searching for another child.
|
|
hr = pxdnLastChild->get_nextSibling(&pxdnChild);
|
|
if (FAILED(hr))
|
|
{
|
|
TraceError("HrGetNextChildElement - get_nextSibling", hr);
|
|
pxdnChild = NULL;
|
|
|
|
// we couldn't get the next child, and we haven't found a result,
|
|
// so we need to barf.
|
|
goto Cleanup;
|
|
}
|
|
// maybe there was a next node, maybe there wasn't
|
|
|
|
hr = HrGetChildElement(pxdnChild, pszNodeName, &pxdnResult);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
Assert(FImplies(S_OK == hr, pxdnResult));
|
|
Assert(FImplies(S_OK != hr, !pxdnResult));
|
|
|
|
SAFE_RELEASE(pxdnChild);
|
|
|
|
*ppxdn = pxdnResult;
|
|
|
|
TraceErrorOptional("HrGetNextChildElement", hr, (S_FALSE == hr));
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: HrGetChildElement
|
|
//
|
|
// Purpose: Returns the first child element of a given name.
|
|
//
|
|
// Arguments:
|
|
// pxdn Node to start searching at. This node and its
|
|
// subsequent siblings are searched.
|
|
// Note that this MAY be null, in which case this
|
|
// function doesn't do much...
|
|
//
|
|
// pszNodeName Name of the desired child node.
|
|
//
|
|
// ppxdn On return, contiains an AddRef()'d IXMLDOMNode
|
|
// pointer to the child element, if one of the
|
|
// given name exists.
|
|
//
|
|
// Returns:
|
|
// S_OK if a matching child node has been found. *ppxdn contains
|
|
// a reference to this found node
|
|
// S_FALSE if no matching child was found. *ppxdn is set to NULL.
|
|
//
|
|
// Notes:
|
|
// To retrieve a child element declared as <xxxx:yyyy>, pszNodeName
|
|
// must be "yyyy".
|
|
// TODO: make sure the elements considered match a specified
|
|
// namespace uri.
|
|
HRESULT
|
|
HrGetChildElement(IXMLDOMNode * pxdn,
|
|
LPCWSTR pszNodeName,
|
|
IXMLDOMNode ** ppxdn)
|
|
{
|
|
Assert(ppxdn);
|
|
Assert(pszNodeName);
|
|
|
|
HRESULT hr;
|
|
IXMLDOMNode * pxdnChild;
|
|
|
|
if (pxdn)
|
|
{
|
|
pxdn->AddRef();
|
|
}
|
|
|
|
// in case pxdnChild is NULL
|
|
hr = S_FALSE;
|
|
pxdnChild = pxdn;
|
|
|
|
while (pxdnChild)
|
|
{
|
|
// invaiant: pxdnChild is an addref()'d pointer to the current
|
|
// child of interest
|
|
IXMLDOMNode * pxdnTemp;
|
|
DOMNodeType dnt;
|
|
|
|
pxdnTemp = NULL;
|
|
|
|
// get the DOMNodeType
|
|
hr = pxdnChild->get_nodeType(&dnt);
|
|
if (FAILED(hr))
|
|
{
|
|
TraceError("HrGetChildElement - get_nodeType", hr);
|
|
|
|
goto Error;
|
|
}
|
|
|
|
if (NODE_ELEMENT == dnt)
|
|
{
|
|
if (FIsThisTheNodeName(pxdnChild, pszNodeName))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
hr = pxdnChild->get_nextSibling(&pxdnTemp);
|
|
if (FAILED(hr))
|
|
{
|
|
TraceError("HrGetChildElement - get_nextSibling", hr);
|
|
|
|
// we couldn't get the next child, and we haven't found a result,
|
|
// so we need to barf.
|
|
goto Error;
|
|
}
|
|
Assert((S_OK == hr) || (S_FALSE == hr));
|
|
Assert(FImplies(S_OK == hr, pxdnTemp));
|
|
Assert(FImplies(S_FALSE == hr, !pxdnTemp));
|
|
|
|
// note: if hr is S_FALSE, we're done
|
|
|
|
pxdnChild->Release();
|
|
pxdnChild = pxdnTemp;
|
|
}
|
|
|
|
Assert((S_OK == hr) || (S_FALSE == hr));
|
|
Assert(FImplies(S_OK == hr, pxdnChild));
|
|
Assert(FImplies(S_FALSE == hr, !pxdnChild));
|
|
|
|
// post-loop-condition: pxdnChild is an addref'd pointer to the
|
|
// matching child, of type NODE_ELEMENT.
|
|
// if there is no match, pxdnChild is NULL.
|
|
|
|
Cleanup:
|
|
*ppxdn = pxdnChild;
|
|
|
|
TraceErrorOptional("HrGetChildElement", hr, (S_FALSE == hr));
|
|
return hr;
|
|
|
|
Error:
|
|
hr = E_FAIL;
|
|
|
|
SAFE_RELEASE(pxdnChild);
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Used for Validation duplicate tags in Description Document - Guru
|
|
HRESULT HrIsElementPresentOnce(
|
|
IXMLDOMNode * pxdnRoot,
|
|
LPCWSTR pszNodeName ) {
|
|
|
|
HRESULT hr = S_OK ;
|
|
LONG nNumNodes = 0;
|
|
IXMLDOMNodeList * pxdnlNodes = NULL;
|
|
BSTR bstrXSLPattern = NULL;
|
|
|
|
bstrXSLPattern = SysAllocString(pszNodeName);
|
|
|
|
if (bstrXSLPattern)
|
|
{
|
|
hr = pxdnRoot->selectNodes(bstrXSLPattern, &pxdnlNodes);
|
|
if(hr == S_OK)
|
|
{
|
|
hr = pxdnlNodes->get_length(&nNumNodes);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
if ( nNumNodes == 1 )
|
|
hr = S_OK;
|
|
else {
|
|
hr = E_FAIL ;
|
|
//hr = S_FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
TraceError("HrIsElementPresentOnce(): "
|
|
"Failed to allocate BSTR for XSL pattern",
|
|
hr);
|
|
}
|
|
|
|
SAFE_RELEASE(pxdnlNodes);
|
|
SysFreeString(bstrXSLPattern);
|
|
|
|
TraceErrorOptional("HrIsElementPresentOnce", hr, (S_FALSE == hr));
|
|
return hr;
|
|
}
|
|
|
|
// Used for Validation duplicate tags in Description Document - Guru
|
|
HRESULT HrCheckForDuplicateElement(IXMLDOMNode * pxdnRoot,
|
|
LPCWSTR pszNodeName ) {
|
|
|
|
HRESULT hr = S_OK ;
|
|
LONG nNumNodes = 0;
|
|
IXMLDOMNodeList * pxdnlNodes = NULL;
|
|
BSTR bstrXSLPattern = NULL;
|
|
|
|
bstrXSLPattern = SysAllocString(pszNodeName);
|
|
|
|
if (bstrXSLPattern)
|
|
{
|
|
hr = pxdnRoot->selectNodes(bstrXSLPattern, &pxdnlNodes);
|
|
if(hr == S_OK)
|
|
{
|
|
hr = pxdnlNodes->get_length(&nNumNodes);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
if ( nNumNodes > 1 )
|
|
{
|
|
hr = E_FAIL;
|
|
TraceError("HrCheckForDuplicateElement(): "
|
|
"Duplicate Tag Present",
|
|
hr);
|
|
}
|
|
else {
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
TraceError("HrCheckForDuplicateElement(): "
|
|
"Failed to allocate BSTR for XSL pattern",
|
|
hr);
|
|
}
|
|
|
|
SAFE_RELEASE(pxdnlNodes);
|
|
SysFreeString(bstrXSLPattern);
|
|
|
|
TraceErrorOptional("HrCheckForDuplicateElement", hr, (S_FALSE == hr));
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: HrGetNestedChildElement
|
|
//
|
|
// Purpose: Returns the nth-level child of a particular element,
|
|
// given the name of the element and the name of each
|
|
// intermediate element.
|
|
//
|
|
// Arguments:
|
|
// pxdnRoot The element which should contain the specified
|
|
// children
|
|
//
|
|
// arypszTokens A serial list of child element names that uniquely
|
|
// describe the desired element. See notes below.
|
|
//
|
|
// cTokens The number of element names contained in
|
|
// arypszTokens.
|
|
//
|
|
// ppxdn On return, contiains an AddRef()'d IXMLDOMNode
|
|
// pointer to the child element, if one of the
|
|
// given name exists.
|
|
//
|
|
// Returns:
|
|
// S_OK if a matching child node has been found. *ppxdn contains
|
|
// a reference to this found node
|
|
// S_FALSE if no matching child was found. *ppxdn is set to NULL.
|
|
//
|
|
// Notes:
|
|
// To retrieve a child element declared as <xxxx:yyyy>, pszNodeName
|
|
// must be "yyyy".
|
|
// TODO: make it ensure that there is only ONE matching node!
|
|
// also...
|
|
// TODO: make sure the elements considered match a specified
|
|
// namespace uri.
|
|
//
|
|
// overview of function:
|
|
// given a list of element names and a root element, it returns the
|
|
// "n"th child element of the given element.
|
|
// e.g., if the "root" element passed in was <foo> and the tokens
|
|
// passed in were ["bar","car","dar" and "ear"], and the document
|
|
// looked like the following,
|
|
// <foo> <--- pxdnRoot
|
|
// <bar>
|
|
// <car>
|
|
// <dar>
|
|
// <ear/>
|
|
// </dar>
|
|
// </car>
|
|
// </bar>
|
|
// </foo>
|
|
// this returns the IXMLDOMNode reference to "<ear/>"
|
|
//
|
|
// if more than one of the desired name exists at a given level,
|
|
// this will chose the first element.
|
|
// <foo>
|
|
// <bar>...</bar> <!-- this one is returned -->
|
|
// <bar>...</bar>
|
|
// </foo>
|
|
//
|
|
// TODO: only return an element if its namespace matches the desired
|
|
// namespace uri
|
|
//
|
|
// pszNameSpaceURI = URI of the namespace that each element in the
|
|
// token list must be defined in
|
|
HRESULT
|
|
HrGetNestedChildElement(IXMLDOMNode * pxdnRoot,
|
|
const LPCWSTR * arypszTokens,
|
|
const ULONG cTokens,
|
|
IXMLDOMNode ** ppxdn)
|
|
{
|
|
HRESULT hr;
|
|
IXMLDOMNode * pxdnCurrent;
|
|
ULONG i;
|
|
|
|
Assert(pxdnRoot);
|
|
Assert(arypszTokens);
|
|
Assert(cTokens);
|
|
Assert(ppxdn);
|
|
|
|
pxdnCurrent = pxdnRoot;
|
|
pxdnCurrent->AddRef();
|
|
|
|
i = 0;
|
|
for ( ; i < cTokens; ++i)
|
|
{
|
|
Assert(arypszTokens[i]);
|
|
|
|
IXMLDOMNode * pxdnChild;
|
|
|
|
pxdnChild = NULL;
|
|
|
|
|
|
hr = HrGetFirstChildElement(pxdnCurrent, arypszTokens[i], &pxdnChild);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Error;
|
|
}
|
|
else if (S_FALSE == hr)
|
|
{
|
|
// the child of the specified name doesn't exist. well, then...
|
|
// we return S_FALSE and NULL in this case, which is what
|
|
// we happen to have in hr and will put in pxdnCurrent
|
|
goto Error;
|
|
}
|
|
Assert(pxdnChild);
|
|
|
|
pxdnCurrent->Release();
|
|
pxdnCurrent = pxdnChild;
|
|
}
|
|
Assert(pxdnCurrent);
|
|
|
|
// pxdnCurrent is the desired child
|
|
|
|
Cleanup:
|
|
Assert(FImplies((S_OK == hr), pxdnCurrent));
|
|
Assert(FImplies(S_OK != hr, !pxdnCurrent));
|
|
|
|
*ppxdn = pxdnCurrent;
|
|
|
|
TraceErrorOptional("HrGetNestedChildElement", hr, (S_FALSE == hr));
|
|
return hr;
|
|
|
|
Error:
|
|
// we didn't find a match
|
|
SAFE_RELEASE(pxdnCurrent);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
// return values:
|
|
// S_OK all values have been read
|
|
// error some element couldn't be read.
|
|
// the returned array is undefined.
|
|
HRESULT
|
|
HrReadElementWithParseData (IXMLDOMNode * pxdn,
|
|
const ULONG cElems,
|
|
const DevicePropertiesParsingStruct dppsInfo [],
|
|
LPCWSTR pszBaseUrl,
|
|
LPWSTR arypszReadValues [])
|
|
{
|
|
Assert(pxdn);
|
|
Assert(cElems);
|
|
Assert(dppsInfo);
|
|
Assert(arypszReadValues);
|
|
|
|
HRESULT hr = S_OK;
|
|
ULONG i;
|
|
|
|
::ZeroMemory(arypszReadValues, sizeof(LPWSTR *) * cElems);
|
|
|
|
i = 0;
|
|
for ( ; i < cElems; ++i )
|
|
{
|
|
BSTR bstrTemp;
|
|
|
|
bstrTemp = NULL;
|
|
|
|
hr = HrGetTextValueFromChildElement(pxdn,
|
|
&(dppsInfo[i].m_pszTagName),
|
|
1,
|
|
&bstrTemp);
|
|
Assert(FImplies((S_FALSE == hr), !bstrTemp));
|
|
if (S_OK == hr)
|
|
{
|
|
Assert(bstrTemp);
|
|
|
|
if (dppsInfo[i].m_fIsUrl)
|
|
{
|
|
LPWSTR pszTemp;
|
|
|
|
pszTemp = NULL;
|
|
if (pszBaseUrl)
|
|
{
|
|
// we have to combine base and relative urls
|
|
hr = HrCombineUrl(pszBaseUrl, bstrTemp, &pszTemp);
|
|
}
|
|
else
|
|
{
|
|
// just copy and validate the url
|
|
hr = HrCopyAndValidateUrl(bstrTemp, &pszTemp);
|
|
if (S_FALSE == hr)
|
|
{
|
|
// invalid urls aren't acceptable
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
arypszReadValues[i] = pszTemp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// just copy the value
|
|
arypszReadValues[i] = WszAllocateAndCopyWsz(bstrTemp);
|
|
if (!(arypszReadValues[i]))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
::SysFreeString(bstrTemp);
|
|
}
|
|
// note: hr may have been changed by the above code
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
Assert(!(arypszReadValues[i]));
|
|
goto Error;
|
|
}
|
|
TraceTag(ttidUPnPDescriptionDoc,"XML Tag - %S - %S",dppsInfo[i].m_pszTagName,arypszReadValues[i]);
|
|
}
|
|
|
|
if (S_FALSE == hr)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
Cleanup:
|
|
TraceError("HrReadElementWithParseData", hr);
|
|
return hr;
|
|
|
|
Error:
|
|
// we need to free the strings we've already allocated
|
|
{
|
|
ULONG j;
|
|
|
|
// i is the number of elements successfully read above
|
|
j = 0;
|
|
for ( ; j < i; ++j )
|
|
{
|
|
Assert(arypszReadValues[j]);
|
|
|
|
delete [] arypszReadValues[j];
|
|
|
|
arypszReadValues[j] = NULL;
|
|
}
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Used for Validation duplicate tags in Description Document - Guru
|
|
// If the element is optional we check if the Tag is present At most Once
|
|
// IF the element is required we check if its present exactly once
|
|
HRESULT HrAreElementTagsValid(IXMLDOMNode *pxdnRoot,
|
|
const ULONG cElems,
|
|
const DevicePropertiesParsingStruct dppsInfo [])
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG i;
|
|
|
|
i = 0;
|
|
for ( ; i < cElems; ++i )
|
|
{
|
|
if (dppsInfo[i].m_fOptional)
|
|
{
|
|
hr = HrCheckForDuplicateElement(pxdnRoot,dppsInfo[i].m_pszTagName);
|
|
}
|
|
else {
|
|
hr = HrIsElementPresentOnce(pxdnRoot,dppsInfo[i].m_pszTagName);
|
|
}
|
|
if(FAILED(hr))
|
|
break;
|
|
}
|
|
|
|
|
|
TraceError("HrAreElementTagsValid", hr);
|
|
return hr;
|
|
}
|
|
|
|
// return TRUE if all of the non-optional values (as specified by the
|
|
// m_fOptional in dppsInfo) are non-null in arypszReadValues.
|
|
// equivalently, it returns FALSE if there exists a value marked as
|
|
// required (m_fOptional == FALSE) that is NULL in arypszReadValues.
|
|
BOOL
|
|
fAreReadValuesComplete (const ULONG cElems,
|
|
const DevicePropertiesParsingStruct dppsInfo [],
|
|
const LPCWSTR arypszReadValues [])
|
|
{
|
|
ULONG i;
|
|
BOOL fResult;
|
|
|
|
fResult = TRUE;
|
|
i = 0;
|
|
for ( ; i < cElems; ++i )
|
|
{
|
|
if (!dppsInfo[i].m_fOptional)
|
|
{
|
|
if (!(arypszReadValues[i]))
|
|
{
|
|
fResult = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fResult;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: HrGetTextValueFromAttribute
|
|
//
|
|
// Purpose: Retrieves the text value of an attribute on a DOM element node
|
|
//
|
|
// Arguments:
|
|
// pxdn [in] XML DOM Node representing the element whose
|
|
// attribute is to be read
|
|
// szAttrName [in] Name of the attribute
|
|
// pbstrAttrValue [in] Receives the attribute text value.
|
|
//
|
|
// Returns:
|
|
// If the function successfully reads the attribute value, the return value
|
|
// is S_OK and the attribute value is returned at pbstrAttrValue. If the
|
|
// attribute does not exist on the element, the function returns S_FALSE,
|
|
// and NULL at pbstrAttrValue. Otherwise the function returns one of the
|
|
// error codes defined in WinError.h.
|
|
//
|
|
// Author: spather 2000/11/9
|
|
//
|
|
// Notes:
|
|
//
|
|
HRESULT
|
|
HrGetTextValueFromAttribute(
|
|
IN IXMLDOMNode * pxdn,
|
|
IN LPCWSTR szAttrName,
|
|
OUT BSTR * pbstrAttrValue)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IXMLDOMElement * pxde = NULL;
|
|
BSTR bstrValue = NULL;
|
|
|
|
hr = pxdn->QueryInterface(IID_IXMLDOMElement,
|
|
(void **) &pxde);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
BSTR bstrAttrName = NULL;
|
|
|
|
Assert(pxde);
|
|
|
|
bstrAttrName = SysAllocString(szAttrName);
|
|
|
|
if (bstrAttrName)
|
|
{
|
|
VARIANT varValue;
|
|
|
|
VariantInit(&varValue);
|
|
|
|
hr = pxde->getAttribute(bstrAttrName,
|
|
&varValue);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (VT_NULL == varValue.vt)
|
|
{
|
|
Assert(S_FALSE == hr);
|
|
}
|
|
else if (VT_BSTR == varValue.vt)
|
|
{
|
|
bstrValue = V_BSTR(&varValue);
|
|
}
|
|
else
|
|
{
|
|
Assert(FALSE); // Should never be here.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TraceError("HrGetTextValueFromAttribute(): "
|
|
"Failed to get attribute value",
|
|
hr);
|
|
}
|
|
|
|
SysFreeString(bstrAttrName);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
TraceError("HrGetTextValueFromAttribute(): "
|
|
"Failed to allocate attribute name BSTR",
|
|
hr);
|
|
}
|
|
|
|
pxde->Release();
|
|
}
|
|
else
|
|
{
|
|
TraceError("HrGetTextValueFromAttribute(): "
|
|
"Failed to get IXMLDOMElement interface",
|
|
hr);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*pbstrAttrValue = bstrValue;
|
|
}
|
|
else
|
|
{
|
|
if (bstrValue)
|
|
{
|
|
SysFreeString(bstrValue);
|
|
}
|
|
}
|
|
|
|
TraceHr(ttidError, FAL, hr, (hr == S_FALSE), "HrGetTextValueFromAttribute");
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* Function: HrSetTextAttribute()
|
|
*
|
|
* Purpose: Sets an text-valued attribute on an XML element.
|
|
*
|
|
* Arguments:
|
|
* pElement [in] The element on which to set the attribute
|
|
* pcwszAttrName [in] The attribute name
|
|
* pcwszValue [in] The value to give the attribute
|
|
*
|
|
* Returns:
|
|
* S_OK if successful, other HRESULT otherwise.
|
|
*/
|
|
|
|
HRESULT
|
|
HrSetTextAttribute(
|
|
IXMLDOMElement * pElement,
|
|
LPCWSTR pcwszAttrName,
|
|
LPCWSTR pcwszValue)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
VARIANT vValue;
|
|
BSTR bstrValue, bstrAttrName;
|
|
|
|
// Allocate BSTRs for the attribute name and value.
|
|
|
|
bstrValue = SysAllocString(pcwszValue);
|
|
|
|
if (bstrValue)
|
|
{
|
|
bstrAttrName = SysAllocString(pcwszAttrName);
|
|
|
|
if (!bstrAttrName)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
SysFreeString(bstrValue);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// danielwe: Bug 144883
|
|
VariantInit(&vValue);
|
|
|
|
vValue.vt = VT_BSTR;
|
|
V_BSTR(&vValue) = bstrValue;
|
|
|
|
hr = pElement->setAttribute(bstrAttrName, vValue);
|
|
|
|
SysFreeString(bstrValue);
|
|
SysFreeString(bstrAttrName);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: HrCreateElementWithType()
|
|
*
|
|
* Purpose: Creates an XML element containing a binary value that is
|
|
* encoded in bin.base64.
|
|
*
|
|
* Arguments:
|
|
* pDoc [in] Document in which to create the element
|
|
* pcwszElementName [in] Name for the new element
|
|
* pszDataType [in] The type of the data encoded in the element.
|
|
* varData [in] Data to insert as the element's value
|
|
* ppElement [out] Address at which to place the pointer to the
|
|
* new element object
|
|
*
|
|
* Returns:
|
|
* S_OK if successful, other HRESULT otherwise.
|
|
*
|
|
* Notes:
|
|
* This function does not actually insert the new element into the document
|
|
* tree.
|
|
*/
|
|
|
|
HRESULT
|
|
HrCreateElementWithType(
|
|
IN IXMLDOMDocument * pDoc,
|
|
IN LPCWSTR pcwszElementName,
|
|
IN LPCWSTR pszDataType,
|
|
IN VARIANT varData,
|
|
OUT IXMLDOMElement ** ppElement)
|
|
{
|
|
Assert(pDoc);
|
|
Assert(pcwszElementName);
|
|
Assert(pszDataType);
|
|
Assert(ppElement);
|
|
|
|
HRESULT hr;
|
|
BSTR bstrElementName;
|
|
|
|
hr = S_OK;
|
|
*ppElement = NULL;
|
|
|
|
bstrElementName = SysAllocString(pcwszElementName);
|
|
if (bstrElementName)
|
|
{
|
|
IXMLDOMElement * pElement = NULL;
|
|
|
|
hr = pDoc->createElement(bstrElementName,
|
|
&pElement);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
BSTR bstrDataType;
|
|
|
|
bstrDataType = ::SysAllocString(pszDataType);
|
|
if (bstrDataType)
|
|
{
|
|
hr = pElement->put_dataType(bstrDataType);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pElement->put_nodeTypedValue(varData);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppElement = pElement;
|
|
pElement->AddRef();
|
|
}
|
|
else
|
|
{
|
|
TraceError("HrCreateElementWithType(): "
|
|
"put_nodeTypedValue", hr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TraceError("HrCreateElementWithType(): "
|
|
"put_dataType", hr);
|
|
}
|
|
|
|
::SysFreeString(bstrDataType);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
TraceError("HrCreateElementWithType(): "
|
|
"SysAllocString", hr);
|
|
}
|
|
|
|
pElement->Release();
|
|
}
|
|
else
|
|
{
|
|
TraceError("HrCreateElementWithType(): "
|
|
"Could not create element", hr);
|
|
}
|
|
|
|
::SysFreeString(bstrElementName);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
TraceError("HrCreateElementWithType(): "
|
|
"Could not allocate BSTR", hr);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: HrCreateElementWithTextValue()
|
|
*
|
|
* Purpose: Creates an XML element containing a text string as
|
|
* a value.
|
|
*
|
|
* Arguments:
|
|
* pDoc [in] Document in which to create the element
|
|
* pcwszElementName [in] Name for the new element
|
|
* pcwszTextValue [in] Text to insert as the element's value
|
|
* ppElement [out] Address at which to place the pointer to the
|
|
* new element object
|
|
*
|
|
* Returns:
|
|
* S_OK if successful, other HRESULT otherwise.
|
|
*
|
|
* Notes:
|
|
* This function does not actually insert the new element into the document
|
|
* tree.
|
|
*/
|
|
|
|
HRESULT
|
|
HrCreateElementWithTextValue(
|
|
IN IXMLDOMDocument * pDoc,
|
|
IN LPCWSTR pcwszElementName,
|
|
IN LPCWSTR pcwszTextValue,
|
|
OUT IXMLDOMElement ** ppElement)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IXMLDOMElement * pElement = NULL;
|
|
BSTR bstrElementName, bstrTextValue;
|
|
|
|
*ppElement = NULL;
|
|
|
|
// Allocate BSTRs for the element name and value.
|
|
|
|
bstrElementName = SysAllocString(pcwszElementName);
|
|
|
|
if (bstrElementName)
|
|
{
|
|
bstrTextValue = SysAllocString(pcwszTextValue);
|
|
|
|
if (!bstrTextValue)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
SysFreeString(bstrElementName);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pDoc->createElement(bstrElementName,
|
|
&pElement);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IXMLDOMText * pText = NULL;
|
|
|
|
hr = pDoc->createTextNode(bstrTextValue,
|
|
&pText);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
VARIANT vAfter;
|
|
|
|
// danielwe: Bug 144882
|
|
VariantInit(&vAfter);
|
|
|
|
vAfter.vt = VT_EMPTY;
|
|
|
|
hr = pElement->insertBefore(pText,
|
|
vAfter,
|
|
NULL);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
TraceError("HrCreateElementWithTextValue(): "
|
|
"Failed to insert text node", hr);
|
|
}
|
|
|
|
pText->Release();
|
|
}
|
|
else
|
|
{
|
|
TraceError("HrCreateElementWithTextValue(): "
|
|
"Failed to create text node", hr);
|
|
}
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppElement = pElement;
|
|
pElement->AddRef();
|
|
}
|
|
|
|
pElement->Release();
|
|
}
|
|
else
|
|
{
|
|
TraceError("HrCreateElementWithTextValue(): "
|
|
"Could not create element", hr);
|
|
}
|
|
|
|
SysFreeString(bstrElementName);
|
|
SysFreeString(bstrTextValue);
|
|
}
|
|
else
|
|
{
|
|
TraceError("HrCreateElementWithTextValue(): "
|
|
"Could not allocate BSTRs", hr);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* Function: HrCreateElement()
|
|
*
|
|
* Purpose: Create an XML Element DOM object
|
|
*
|
|
* Arguments:
|
|
* pxdd [in] The document object in whose context the
|
|
* element will be created
|
|
* pcwszElementName [in] Name of the element to be created
|
|
* pcwszNamespaceURI [in] URI identifying the namespace to which
|
|
* the element name belongs
|
|
* ppxdnNewElt [out] The newly created element object
|
|
*
|
|
* Return Value:
|
|
* S_OK if successful, other HRESULT otherwise.
|
|
*
|
|
* Notes:
|
|
* (none)
|
|
*/
|
|
|
|
HRESULT
|
|
HrCreateElement(
|
|
IN IXMLDOMDocument * pxdd,
|
|
IN LPCWSTR pcwszElementName,
|
|
IN LPCWSTR pcwszNamespaceURI,
|
|
OUT IXMLDOMNode ** ppxdnNewElt)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IXMLDOMNode * pxdnNewElt = NULL;
|
|
BSTR bstrElementName = NULL;
|
|
BSTR bstrNamespaceURI = NULL;
|
|
|
|
Assert(pxdd);
|
|
Assert(pcwszElementName);
|
|
Assert(ppxdnNewElt);
|
|
|
|
// Allocate BSTRs
|
|
|
|
bstrElementName = SysAllocString(pcwszElementName);
|
|
|
|
if (bstrElementName)
|
|
{
|
|
if (pcwszNamespaceURI)
|
|
{
|
|
bstrNamespaceURI = SysAllocString(pcwszNamespaceURI);
|
|
|
|
if (NULL == bstrNamespaceURI)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
TraceError("HrCreateElement(): "
|
|
"Failed to allocate memory for name string",
|
|
hr);
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
VARIANT varNodeType;
|
|
|
|
VariantInit(&varNodeType);
|
|
varNodeType.vt = VT_I4;
|
|
V_I4(&varNodeType) = (int) NODE_ELEMENT;
|
|
|
|
hr = pxdd->createNode(varNodeType,
|
|
bstrElementName,
|
|
bstrNamespaceURI,
|
|
&pxdnNewElt);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
TraceError("HrCreateElement(): "
|
|
"Failed to create element node",
|
|
hr);
|
|
}
|
|
|
|
|
|
if (bstrNamespaceURI)
|
|
{
|
|
SysFreeString(bstrNamespaceURI);
|
|
}
|
|
}
|
|
|
|
|
|
SysFreeString(bstrElementName);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
TraceError("HrCreateElement(): "
|
|
"Failed to allocate memory for name string",
|
|
hr);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppxdnNewElt = pxdnNewElt;
|
|
}
|
|
|
|
TraceError("HrCreateElement(): "
|
|
"Exiting",
|
|
hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: HrAppendProcessingInstruction()
|
|
*
|
|
* Purpose: Append a processing instruction to the DOM document
|
|
*
|
|
* Arguments:
|
|
* pxdd [in] The document object in whose context the
|
|
* element will be created
|
|
* pcwszName [in] Name of the processing instruction to be created
|
|
* pcwszValue [in] The text value of the p.i.
|
|
*
|
|
* Return Value:
|
|
* S_OK if successful, other HRESULT otherwise.
|
|
*
|
|
* Notes:
|
|
* (none)
|
|
*/
|
|
|
|
HRESULT
|
|
HrAppendProcessingInstruction(
|
|
IN IXMLDOMDocument * pxdd,
|
|
IN LPCWSTR pcwszName,
|
|
IN LPCWSTR pcwszValue)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
Assert(pxdd);
|
|
Assert(pcwszName);
|
|
Assert(pcwszValue);
|
|
|
|
IXMLDOMProcessingInstruction* pxdpi = NULL;
|
|
BSTR bstrTarget = SysAllocString(pcwszName);
|
|
BSTR bstrData = SysAllocString(pcwszValue);
|
|
if (bstrTarget && bstrData)
|
|
{
|
|
hr = pxdd->createProcessingInstruction(bstrTarget, bstrData, &pxdpi);
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
SysFreeString(bstrTarget);
|
|
SysFreeString(bstrData);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IXMLDOMNode* pxdn = NULL;
|
|
hr = pxdpi->QueryInterface(IID_IXMLDOMNode, (void**)&pxdn);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pxdd->appendChild(pxdn, NULL);
|
|
pxdn->Release();
|
|
}
|
|
}
|
|
|
|
if (pxdpi)
|
|
{
|
|
pxdpi->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|