[PATCH] msxml3: Implement mxwriter ISAXContentHandler domdoc destination.

Jefferson Carpenter jefferson at aoeu2code.com
Wed Jan 20 22:48:05 CST 2021


Here's an implementation of DOMDocument output for mxwriter.  It only 
implements the ISAXContentHandler mxwriter interface.

Supersedes 198351.

thanks,
Jefferson
-------------- next part --------------
From 07fd479930b6ea8d8dceddd9dd1924a99601a1f6 Mon Sep 17 00:00:00 2001
From: Jefferson Carpenter <jeffersoncarpenter2 at gmail.com>
Date: Wed, 20 Jan 2021 01:07:44 +0000
Subject: [PATCH] msxml3: Implement mxwriter ISAXContentHandler domdoc
 destination.

Signed-off-by: Jefferson Carpenter <jeffersoncarpenter2 at gmail.com>
---
 dlls/msxml3/mxwriter.c        | 406 +++++++++++++++++++++++++++-------
 dlls/msxml3/tests/saxreader.c | 217 ++++++++++++++++++
 2 files changed, 547 insertions(+), 76 deletions(-)

diff --git a/dlls/msxml3/mxwriter.c b/dlls/msxml3/mxwriter.c
index 029e9aac0d0..0b8eddc44cd 100644
--- a/dlls/msxml3/mxwriter.c
+++ b/dlls/msxml3/mxwriter.c
@@ -142,6 +142,13 @@ typedef enum
     EscapeText
 } escape_mode;
 
+typedef enum
+{
+    OutputIStream,
+    OutputString,
+    OutputDOMDocument,
+} output_type;
+
 typedef struct
 {
     struct list entry;
@@ -193,7 +200,11 @@ typedef struct
        we don't have to close */
     BSTR element;
 
+    IXMLDOMNode *cur_node; /* current node for DOMDocument destination */
+
+    output_type output_ty;
     IStream *dest;
+    IXMLDOMDocument *dest_doc;
 
     output_buffer buffer;
 } mxwriter;
@@ -715,6 +726,32 @@ static HRESULT writer_get_property(const mxwriter *writer, mxwriter_prop propert
     return S_OK;
 }
 
+static void push_cur_node(mxwriter *writer, IXMLDOMNode *node)
+{
+    if (writer->cur_node) {
+        IXMLDOMNode_Release(writer->cur_node);
+    }
+
+    IXMLDOMNode_AddRef(node);
+    writer->cur_node = node;
+}
+
+static HRESULT pop_cur_node(mxwriter *writer)
+{
+    HRESULT hr;
+    IXMLDOMNode *parent_node = NULL;
+
+    if (!writer->cur_node) return E_INVALIDARG;
+
+    hr = IXMLDOMNode_get_parentNode(writer->cur_node, &parent_node);
+    if (FAILED(hr)) return hr;
+
+    IXMLDOMNode_Release(writer->cur_node);
+    writer->cur_node = parent_node;
+
+    return S_OK;
+}
+
 static inline mxwriter *impl_from_IMXWriter(IMXWriter *iface)
 {
     return CONTAINING_RECORD(iface, mxwriter, IMXWriter_iface);
@@ -863,6 +900,9 @@ static ULONG WINAPI mxwriter_Release(IMXWriter *iface)
         free_output_buffer(&This->buffer);
 
         if (This->dest) IStream_Release(This->dest);
+        while (!FAILED(pop_cur_node(This)));
+        if (This->dest_doc) IXMLDOMDocument_Release(This->dest_doc);
+
         SysFreeString(This->version);
         SysFreeString(This->encoding);
 
@@ -926,8 +966,14 @@ static HRESULT WINAPI mxwriter_put_output(IMXWriter *iface, VARIANT dest)
     case VT_EMPTY:
     {
         if (This->dest) IStream_Release(This->dest);
+        while (!FAILED(pop_cur_node(This)));
+        if (This->dest_doc) IXMLDOMDocument_Release(This->dest_doc);
+
         This->dest = NULL;
+        This->dest_doc = NULL;
+
         reset_output_buffer(This);
+        This->output_ty = OutputString;
         break;
     }
     case VT_UNKNOWN:
@@ -941,13 +987,41 @@ static HRESULT WINAPI mxwriter_put_output(IMXWriter *iface, VARIANT dest)
             reset_output_buffer(This);
 
             if (This->dest) IStream_Release(This->dest);
+            while (!FAILED(pop_cur_node(This)));
+            if (This->dest_doc) IXMLDOMDocument_Release(This->dest_doc);
+
             This->dest = stream;
+            This->dest_doc = NULL;
+            This->output_ty = OutputIStream;
             break;
         }
 
         FIXME("unhandled interface type for VT_UNKNOWN destination\n");
         return E_NOTIMPL;
     }
+    case VT_DISPATCH:
+    {
+        IXMLDOMDocument *document;
+
+        hr = IDispatch_QueryInterface(V_DISPATCH(&dest), &IID_IXMLDOMDocument, (void**)&document);
+        if (hr == S_OK) {
+            /* Reset output buffer to initial state. */
+            reset_output_buffer(This);
+
+            if (This->dest) IStream_Release(This->dest);
+            while (!FAILED(pop_cur_node(This)));
+            if (This->dest_doc) IXMLDOMDocument_Release(This->dest_doc);
+
+            This->dest = NULL;
+            This->dest_doc = document;
+            push_cur_node(This, (IXMLDOMNode*)document);
+            This->output_ty = OutputDOMDocument;
+            break;
+        }
+
+        FIXME("unhandled interface type for VT_DISPATCH destination\n");
+        return E_NOTIMPL;
+    }
     default:
         FIXME("unhandled destination type %s\n", debugstr_variant(&dest));
         return E_NOTIMPL;
@@ -1223,28 +1297,36 @@ static HRESULT WINAPI SAXContentHandler_startDocument(ISAXContentHandler *iface)
 
     TRACE("(%p)\n", This);
 
-    /* If properties have been changed since the last "endDocument" call
-     * we need to reset the output buffer. If we don't the output buffer
-     * could end up with multiple XML documents in it, plus this seems to
-     * be how Windows works.
-     */
-    if (This->prop_changed) {
-        reset_output_buffer(This);
-        This->prop_changed = FALSE;
-    }
+    switch (This->output_ty)
+    {
+    case OutputIStream:
+    case OutputString:
+        /* If properties have been changed since the last "endDocument" call
+         * we need to reset the output buffer. If we don't the output buffer
+         * could end up with multiple XML documents in it, plus this seems to
+         * be how Windows works.
+         */
+        if (This->prop_changed) {
+            reset_output_buffer(This);
+            This->prop_changed = FALSE;
+        }
 
-    if (This->props[MXWriter_OmitXmlDecl] == VARIANT_TRUE) return S_OK;
+        if (This->props[MXWriter_OmitXmlDecl] == VARIANT_TRUE) return S_OK;
 
-    write_prolog_buffer(This);
+        write_prolog_buffer(This);
 
-    if (This->dest && This->xml_enc == XmlEncoding_UTF16) {
-        static const char utf16BOM[] = {0xff,0xfe};
+        if (This->dest && This->xml_enc == XmlEncoding_UTF16) {
+            static const char utf16BOM[] = {0xff,0xfe};
 
-        if (This->props[MXWriter_BOM] == VARIANT_TRUE)
-            /* Windows passes a NULL pointer as the pcbWritten parameter and
-             * ignores any error codes returned from this Write call.
-             */
-            IStream_Write(This->dest, utf16BOM, sizeof(utf16BOM), NULL);
+            if (This->props[MXWriter_BOM] == VARIANT_TRUE)
+                /* Windows passes a NULL pointer as the pcbWritten parameter and
+                 * ignores any error codes returned from this Write call.
+                 */
+                IStream_Write(This->dest, utf16BOM, sizeof(utf16BOM), NULL);
+        }
+        break;
+    case OutputDOMDocument:
+        break;
     }
 
     return S_OK;
@@ -1254,7 +1336,15 @@ static HRESULT WINAPI SAXContentHandler_endDocument(ISAXContentHandler *iface)
 {
     mxwriter *This = impl_from_ISAXContentHandler( iface );
     TRACE("(%p)\n", This);
-    This->prop_changed = FALSE;
+    switch (This->output_ty)
+    {
+    case OutputIStream:
+    case OutputString:
+        This->prop_changed = FALSE;
+        break;
+    case OutputDOMDocument:
+        break;
+    }
     return flush_output_buffer(This);
 }
 
@@ -1333,32 +1423,96 @@ static HRESULT WINAPI SAXContentHandler_startElement(
         (nQName == -1 && This->class_version == MSXML6))
         return E_INVALIDARG;
 
-    mxwriter_write_starttag(This, QName, nQName);
+    switch (This->output_ty) {
+    case OutputIStream:
+    case OutputString:
+        mxwriter_write_starttag(This, QName, nQName);
 
-    if (attr)
-    {
-        int length, i, escape;
-        HRESULT hr;
+        if (attr)
+        {
+            int length, i, escape;
+            HRESULT hr;
 
-        hr = ISAXAttributes_getLength(attr, &length);
-        if (FAILED(hr)) return hr;
+            hr = ISAXAttributes_getLength(attr, &length);
+            if (FAILED(hr)) return hr;
 
-        escape = This->props[MXWriter_DisableEscaping] == VARIANT_FALSE ||
+            escape = This->props[MXWriter_DisableEscaping] == VARIANT_FALSE ||
             (This->class_version == MSXML4 || This->class_version == MSXML6);
 
-        for (i = 0; i < length; i++)
-        {
-            int qname_len = 0, value_len = 0;
-            const WCHAR *qname, *value;
+            for (i = 0; i < length; i++)
+            {
+                int qname_len = 0, value_len = 0;
+                const WCHAR *qname, *value;
 
-            hr = ISAXAttributes_getQName(attr, i, &qname, &qname_len);
-            if (FAILED(hr)) return hr;
+                hr = ISAXAttributes_getQName(attr, i, &qname, &qname_len);
+                if (FAILED(hr)) return hr;
+
+                hr = ISAXAttributes_getValue(attr, i, &value, &value_len);
+                if (FAILED(hr)) return hr;
 
-            hr = ISAXAttributes_getValue(attr, i, &value, &value_len);
+                mxwriter_write_attribute(This, qname, qname_len, value, value_len, escape);
+            }
+        }
+        break;
+    case OutputDOMDocument:
+    {
+        HRESULT hr = S_OK;
+        IXMLDOMElement *element = NULL;
+        BSTR tag_name = NULL;
+
+        tag_name = SysAllocStringLen(QName, nQName);
+        if (!tag_name) {
+            hr = E_OUTOFMEMORY;
+            goto done;
+        }
+
+        hr = IXMLDOMDocument_createElement(This->dest_doc, tag_name, &element);
+        if (FAILED(hr)) goto done;
+
+        if (attr) {
+            int length, i;
+            BSTR attribute_name = NULL;
+            VARIANT attribute_value;
+
+            hr = ISAXAttributes_getLength(attr, &length);
             if (FAILED(hr)) return hr;
 
-            mxwriter_write_attribute(This, qname, qname_len, value, value_len, escape);
+            for (i = 0; i < length; i++)
+            {
+                int qname_len = 0, value_len = 0;
+                const WCHAR *qname, *value;
+
+                hr = ISAXAttributes_getQName(attr, i, &qname, &qname_len);
+                if (FAILED(hr)) return hr;
+
+                hr = ISAXAttributes_getValue(attr, i, &value, &value_len);
+                if (FAILED(hr)) return hr;
+
+                attribute_name = SysAllocStringLen(qname, qname_len);
+                if (!attribute_name) goto done;
+
+                V_VT(&attribute_value) = VT_BSTR;
+                V_BSTR(&attribute_value) = SysAllocStringLen(value, value_len);
+                if (!V_BSTR(&attribute_value)) goto done;
+
+                IXMLDOMElement_setAttribute(element, attribute_name, attribute_value);
+
+                SysFreeString(V_BSTR(&attribute_value));
+                SysFreeString(attribute_name);
+            }
         }
+
+        hr = IXMLDOMNode_appendChild(This->cur_node, (IXMLDOMNode*)element, NULL);
+        if (FAILED(hr)) goto done;
+
+        push_cur_node(This, (IXMLDOMNode*)element);
+
+    done:
+        if (element) IXMLDOMElement_Release(element);
+        if (tag_name) SysFreeString(tag_name);
+
+        return hr;
+    }
     }
 
     return S_OK;
@@ -1382,25 +1536,34 @@ static HRESULT WINAPI SAXContentHandler_endElement(
          (nQName == -1 && This->class_version == MSXML6))
         return E_INVALIDARG;
 
-    writer_dec_indent(This);
-
-    if (This->element)
+    switch (This->output_ty)
     {
-        static const WCHAR closeW[] = {'/','>'};
-        write_output_buffer(This, closeW, 2);
-    }
-    else
-    {
-        static const WCHAR closetagW[] = {'<','/'};
-        static const WCHAR gtW[] = {'>'};
+    case OutputIStream:
+    case OutputString:
+        writer_dec_indent(This);
 
-        write_node_indent(This);
-        write_output_buffer(This, closetagW, 2);
-        write_output_buffer(This, QName, nQName);
-        write_output_buffer(This, gtW, 1);
-    }
+        if (This->element)
+        {
+            static const WCHAR closeW[] = {'/','>'};
+            write_output_buffer(This, closeW, 2);
+        }
+        else
+        {
+            static const WCHAR closetagW[] = {'<','/'};
+            static const WCHAR gtW[] = {'>'};
 
-    set_element_name(This, NULL, 0);
+            write_node_indent(This);
+            write_output_buffer(This, closetagW, 2);
+            write_output_buffer(This, QName, nQName);
+            write_output_buffer(This, gtW, 1);
+        }
+
+        set_element_name(This, NULL, 0);
+        break;
+    case OutputDOMDocument:
+        pop_cur_node(This);
+        break;
+    }
 
     return S_OK;
 }
@@ -1416,25 +1579,54 @@ static HRESULT WINAPI SAXContentHandler_characters(
 
     if (!chars) return E_INVALIDARG;
 
-    close_element_starttag(This);
-    set_element_name(This, NULL, 0);
+    switch (This->output_ty) {
+    case OutputIStream:
+    case OutputString:
+        close_element_starttag(This);
+        set_element_name(This, NULL, 0);
 
-    if (!This->cdata)
-        This->text = TRUE;
+        if (!This->cdata)
+            This->text = TRUE;
 
-    if (nchars)
-    {
-        if (This->cdata || This->props[MXWriter_DisableEscaping] == VARIANT_TRUE)
-            write_output_buffer(This, chars, nchars);
-        else
+        if (nchars)
         {
-            int len = nchars;
-            WCHAR *escaped;
+            if (This->cdata || This->props[MXWriter_DisableEscaping] == VARIANT_TRUE)
+                write_output_buffer(This, chars, nchars);
+            else
+            {
+                int len = nchars;
+                WCHAR *escaped;
 
-            escaped = get_escaped_string(chars, EscapeText, &len);
-            write_output_buffer(This, escaped, len);
-            heap_free(escaped);
+                escaped = get_escaped_string(chars, EscapeText, &len);
+                write_output_buffer(This, escaped, len);
+                heap_free(escaped);
+            }
         }
+        break;
+    case OutputDOMDocument:
+    {
+        HRESULT hr = S_OK;
+        IXMLDOMText *text_node = NULL;
+        BSTR text = NULL;
+
+        text = SysAllocStringLen(chars, nchars);
+        if (!text) {
+            hr = E_OUTOFMEMORY;
+            goto done;
+        }
+
+        hr = IXMLDOMDocument_createTextNode(This->dest_doc, text, &text_node);
+        if (FAILED(hr)) goto done;
+
+        hr = IXMLDOMNode_appendChild(This->cur_node, (IXMLDOMNode*)text_node, NULL);
+        if (FAILED(hr)) goto done;
+
+    done:
+        if (text_node) IXMLDOMText_Release(text_node);
+        if (text) SysFreeString(text);
+
+        return hr;
+    }
     }
 
     return S_OK;
@@ -1451,7 +1643,15 @@ static HRESULT WINAPI SAXContentHandler_ignorableWhitespace(
 
     if (!chars) return E_INVALIDARG;
 
-    write_output_buffer(This, chars, nchars);
+    switch (This->output_ty)
+    {
+    case OutputIStream:
+    case OutputString:
+        write_output_buffer(This, chars, nchars);
+        break;
+    case OutputDOMDocument:
+        break;
+    }
 
     return S_OK;
 }
@@ -1471,20 +1671,71 @@ static HRESULT WINAPI SAXContentHandler_processingInstruction(
 
     if (!target) return E_INVALIDARG;
 
-    write_node_indent(This);
-    write_output_buffer(This, openpiW, ARRAY_SIZE(openpiW));
+    switch (This->output_ty) {
+    case OutputIStream:
+    case OutputString:
+        write_node_indent(This);
+        write_output_buffer(This, openpiW, ARRAY_SIZE(openpiW));
 
-    if (*target)
-        write_output_buffer(This, target, ntarget);
+        if (*target)
+            write_output_buffer(This, target, ntarget);
 
-    if (data && *data && ndata)
+        if (data && *data && ndata)
+        {
+            write_output_buffer(This, spaceW, 1);
+            write_output_buffer(This, data, ndata);
+        }
+
+        write_output_buffer(This, closepiW, ARRAY_SIZE(closepiW));
+        This->newline = TRUE;
+        break;
+    case OutputDOMDocument:
     {
-        write_output_buffer(This, spaceW, 1);
-        write_output_buffer(This, data, ndata);
-    }
+        HRESULT hr = S_OK;
+        IXMLDOMNode *first_child = NULL;
+        IXMLDOMProcessingInstruction *processing_instruction = NULL;
+        VARIANT ref_node;
+        BSTR target_bstr = NULL;
+        BSTR data_bstr = NULL;
+
+        target_bstr = SysAllocStringLen(target, ntarget);
+        if (!target_bstr) {
+            hr = E_OUTOFMEMORY;
+            goto done;
+        }
+
+        data_bstr = SysAllocStringLen(data, ndata);
+        if (!data_bstr) {
+            hr = E_OUTOFMEMORY;
+            goto done;
+        }
 
-    write_output_buffer(This, closepiW, ARRAY_SIZE(closepiW));
-    This->newline = TRUE;
+        hr = IXMLDOMDocument_createProcessingInstruction(
+            This->dest_doc, target_bstr, data_bstr, &processing_instruction
+        );
+        if (FAILED(hr)) goto done;
+
+        hr = IXMLDOMDocument_get_firstChild(This->dest_doc, &first_child);
+        if (FAILED(hr)) goto done;
+
+        V_VT(&ref_node) = VT_UNKNOWN;
+        V_UNKNOWN(&ref_node) = (IUnknown*)first_child;
+
+        hr = IXMLDOMDocument_insertBefore(
+            This->dest_doc, (IXMLDOMNode*)processing_instruction, ref_node, NULL
+        );
+        if (FAILED(hr)) goto done;
+
+    done:
+        if (first_child) IXMLDOMNode_Release(first_child);
+        if (processing_instruction)
+            IXMLDOMProcessingInstruction_Release(processing_instruction);
+        if (data_bstr) SysFreeString(data_bstr);
+        if (target_bstr) SysFreeString(target_bstr);
+
+        return hr;
+    }
+    }
 
     return S_OK;
 }
@@ -2651,7 +2902,10 @@ HRESULT MXWriter_create(MSXML_VERSION version, void **ppObj)
     This->text = FALSE;
     This->newline = FALSE;
 
+    This->output_ty = OutputString;
     This->dest = NULL;
+    This->dest_doc = NULL;
+    This->cur_node = NULL;
 
     hr = init_output_buffer(This->xml_enc, &This->buffer);
     if (hr != S_OK) {
diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c
index ab814c5f764..1a89d88ccef 100644
--- a/dlls/msxml3/tests/saxreader.c
+++ b/dlls/msxml3/tests/saxreader.c
@@ -4367,6 +4367,222 @@ static void test_mxwriter_stream(void)
     free_bstrs();
 }
 
+static void test_mxwriter_domdoc(void)
+{
+    IMXWriter *writer;
+    ISAXContentHandler *content;
+    IXMLDOMDocument3 *domdoc;
+    IDispatch *dispatch;
+    ISAXAttributes *sax_attributes;
+    IMXAttributes *mx_attributes;
+    HRESULT hr;
+    VARIANT dest;
+
+    IXMLDOMElement *root = NULL;
+    IXMLDOMNodeList *node_list = NULL;
+    IXMLDOMNode *node = NULL;
+    IXMLDOMNamedNodeMap *attribute_map = NULL;
+    IXMLDOMNode *attribute_node = NULL;
+    LONG list_length = 0;
+    BSTR str;
+
+    /* Create writer and attach DOMDocument output */
+    hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IMXWriter, (void**)&writer);
+    EXPECT_HR(hr, S_OK);
+
+    hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
+    EXPECT_HR(hr, S_OK);
+
+    hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IXMLDOMDocument3, (void**)&domdoc);
+    EXPECT_HR(hr, S_OK);
+
+    hr = IXMLDOMDocument3_QueryInterface(domdoc, &IID_IDispatch, (void**)&dispatch);
+    EXPECT_HR(hr, S_OK);
+
+    V_VT(&dest) = VT_DISPATCH;
+    V_DISPATCH(&dest) = dispatch;
+
+    hr = IMXWriter_put_output(writer, dest);
+    EXPECT_HR(hr, S_OK);
+
+
+    /* Provide writer with events */
+    hr = ISAXContentHandler_startDocument(content);
+    EXPECT_HR(hr, S_OK);
+
+    hr = ISAXContentHandler_processingInstruction(
+        content,
+        L"targ", 4, L"type=\"test sheet\" test=\"something\"", 34
+    );
+    EXPECT_HR(hr, S_OK);
+
+    /* <BankAccount> */
+    hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"BankAccount", 11, NULL);
+    EXPECT_HR(hr, S_OK);
+
+    /* <Number bank="World Bank" branch="Japan">12345</Number> */
+    hr = CoCreateInstance(&CLSID_SAXAttributes60, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IMXAttributes, (void**)&mx_attributes);
+    EXPECT_HR(hr, S_OK);
+
+    hr = IMXAttributes_addAttribute(
+        mx_attributes, _bstr_("uri"), _bstr_("local"),
+        _bstr_("bank"), _bstr_("type"), _bstr_("World Bank")
+    );
+    EXPECT_HR(hr, S_OK);
+
+    hr = IMXAttributes_addAttribute(
+        mx_attributes, _bstr_("uri"), _bstr_("local"),
+        _bstr_("branch"), _bstr_("type"), _bstr_("Japan")
+    );
+    EXPECT_HR(hr, S_OK);
+
+    hr = IMXAttributes_QueryInterface(mx_attributes, &IID_ISAXAttributes, (void**)&sax_attributes);
+    EXPECT_HR(hr, S_OK);
+
+    hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"Number", 6, sax_attributes);
+    EXPECT_HR(hr, S_OK);
+
+    ISAXAttributes_Release(sax_attributes);
+    IMXAttributes_Release(mx_attributes);
+
+    hr = ISAXContentHandler_characters(content, L"12345", 5);
+    EXPECT_HR(hr, S_OK);
+
+    hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"Number", 6);
+    EXPECT_HR(hr, S_OK);
+
+    /* <Name>Captain Ahab</Name> */
+    hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"Name", 4, NULL);
+    EXPECT_HR(hr, S_OK);
+
+    hr = ISAXContentHandler_characters(content, L"Captain Ahab", 12);
+    EXPECT_HR(hr, S_OK);
+
+    hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"Name", 4);
+    EXPECT_HR(hr, S_OK);
+
+    /* </BankAccount> */
+    hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"BankAccount", 11);
+    EXPECT_HR(hr, S_OK);
+
+    hr = ISAXContentHandler_endDocument(content);
+    EXPECT_HR(hr, S_OK);
+
+
+    /* Assert that it created the correct document */
+    hr = IXMLDOMDocument3_get_documentElement(domdoc, &root);
+    EXPECT_HR(hr, S_OK);
+
+    /* <BankAccount> */
+    hr = IXMLDOMElement_get_nodeName(root, &str);
+    EXPECT_HR(hr, S_OK);
+    ok(!lstrcmpW(L"BankAccount", str), "got %s\n", wine_dbgstr_w(str));
+    SysFreeString(str);
+
+    hr = IXMLDOMElement_get_childNodes(root, &node_list);
+    EXPECT_HR(hr, S_OK);
+
+    hr = IXMLDOMNodeList_get_length(node_list, &list_length);
+    EXPECT_HR(hr, S_OK);
+    ok(2 == list_length, "list length %i, expected 1\n", list_length);
+
+    /* <Number */
+    hr = IXMLDOMNodeList_get_item(node_list, 0, &node);
+    EXPECT_HR(hr, S_OK);
+
+    hr = IXMLDOMNode_get_nodeName(node, &str);
+    EXPECT_HR(hr, S_OK);
+    ok(!lstrcmpW(L"Number", str), "got %s\n", wine_dbgstr_w(str));
+    SysFreeString(str);
+
+    hr = IXMLDOMNode_get_attributes(node, &attribute_map);
+    EXPECT_HR(hr, S_OK);
+
+    hr = IXMLDOMNamedNodeMap_get_length(attribute_map, &list_length);
+    EXPECT_HR(hr, S_OK);
+    ok(2 == list_length, "attributes length %i, expected 2\n", list_length);
+
+    /* bank="World Bank" */
+    hr = IXMLDOMNamedNodeMap_get_item(attribute_map, 0, &attribute_node);
+    EXPECT_HR(hr, S_OK);
+
+    hr = IXMLDOMNode_get_nodeName(attribute_node, &str);
+    EXPECT_HR(hr, S_OK);
+    ok(!lstrcmpW(L"bank", str), "got %s\n", wine_dbgstr_w(str));
+    SysFreeString(str);
+
+    hr = IXMLDOMNode_get_text(attribute_node, &str);
+    EXPECT_HR(hr, S_OK);
+    ok(!lstrcmpW(L"World Bank", str), "got %s\n", wine_dbgstr_w(str));
+    SysFreeString(str);
+    IXMLDOMNode_Release(attribute_node);
+
+    /* branch="Japan" */
+    hr = IXMLDOMNamedNodeMap_get_item(attribute_map, 1, &attribute_node);
+    EXPECT_HR(hr, S_OK);
+
+    hr = IXMLDOMNode_get_nodeName(attribute_node, &str);
+    EXPECT_HR(hr, S_OK);
+    ok(!lstrcmpW(L"branch", str), "got %s\n", wine_dbgstr_w(str));
+    SysFreeString(str);
+
+    hr = IXMLDOMNode_get_text(attribute_node, &str);
+    EXPECT_HR(hr, S_OK);
+    ok(!lstrcmpW(L"Japan", str), "got %s\n", wine_dbgstr_w(str));
+    SysFreeString(str);
+    IXMLDOMNode_Release(attribute_node);
+    IXMLDOMNamedNodeMap_Release(attribute_map);
+
+    /* >12345</Number> */
+    hr = IXMLDOMNode_get_text(node, &str);
+    EXPECT_HR(hr, S_OK);
+    ok(!lstrcmpW(L"12345", str), "got %s\n", wine_dbgstr_w(str));
+    SysFreeString(str);
+
+    IXMLDOMNode_Release(node);
+
+    /* <Name>Captain Ahab</Name></BankAccount> */
+    hr = IXMLDOMNodeList_get_item(node_list, 1, &node);
+    EXPECT_HR(hr, S_OK);
+
+    hr = IXMLDOMNode_get_nodeName(node, &str);
+    EXPECT_HR(hr, S_OK);
+    ok(!lstrcmpW(L"Name", str), "got %s\n", wine_dbgstr_w(str));
+    SysFreeString(str);
+
+    hr = IXMLDOMNode_get_text(node, &str);
+    EXPECT_HR(hr, S_OK);
+    ok(!lstrcmpW(L"Captain Ahab", str), "got %s\n", wine_dbgstr_w(str));
+    SysFreeString(str);
+    IXMLDOMNode_Release(node);
+    IXMLDOMNodeList_Release(node_list);
+    IXMLDOMElement_Release(root);
+
+    /* finally test doc output */
+    hr = IXMLDOMDocument3_get_xml(domdoc, &str);
+    EXPECT_HR(hr, S_OK);
+    todo_wine ok(
+        !lstrcmpW(
+            L"<?targ type=\"test sheet\" test=\"something\"?>\r\n"
+            "<BankAccount>"
+            "<Number bank=\"World Bank\" branch=\"Japan\">12345</Number>"
+            "<Name>Captain Ahab</Name>"
+            "</BankAccount>\r\n",
+            str),
+        "got %s\n", wine_dbgstr_w(str));
+    SysFreeString(str);
+
+    IDispatch_Release(dispatch);
+    IXMLDOMDocument3_Release(domdoc);
+    ISAXContentHandler_Release(content);
+    IMXWriter_Release(writer);
+
+    free_bstrs();
+}
+
 static const char *encoding_names[] = {
     "iso-8859-1",
     "iso-8859-2",
@@ -5768,6 +5984,7 @@ START_TEST(saxreader)
         test_mxwriter_properties();
         test_mxwriter_flush();
         test_mxwriter_stream();
+        test_mxwriter_domdoc();
         test_mxwriter_encoding();
         test_mxwriter_dispex();
         test_mxwriter_indent();
-- 
2.26.2



More information about the wine-devel mailing list