Nikolay Sivov : msxml3: Implement output indentation for writer.

Alexandre Julliard julliard at winehq.org
Mon Jul 29 14:01:18 CDT 2013


Module: wine
Branch: master
Commit: b3d85d41d99fbe9518db35761c7e7560d6cdd61b
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=b3d85d41d99fbe9518db35761c7e7560d6cdd61b

Author: Nikolay Sivov <nsivov at codeweavers.com>
Date:   Sat Jul 27 18:27:27 2013 +0400

msxml3: Implement output indentation for writer.

---

 dlls/msxml3/mxwriter.c        |   62 +++++++++++++++++++++++++++++++++++++++--
 dlls/msxml3/tests/saxreader.c |   62 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 119 insertions(+), 5 deletions(-)

diff --git a/dlls/msxml3/mxwriter.c b/dlls/msxml3/mxwriter.c
index d62e6a3..558582d 100644
--- a/dlls/msxml3/mxwriter.c
+++ b/dlls/msxml3/mxwriter.c
@@ -1,7 +1,7 @@
 /*
  *    MXWriter implementation
  *
- * Copyright 2011-2012 Nikolay Sivov for CodeWeavers
+ * Copyright 2011-2013 Nikolay Sivov for CodeWeavers
  * Copyright 2011 Thomas Mullaly
  *
  * This library is free software; you can redistribute it and/or
@@ -43,6 +43,7 @@ static const WCHAR emptyW[] = {0};
 static const WCHAR spaceW[] = {' '};
 static const WCHAR quotW[]  = {'\"'};
 static const WCHAR closetagW[] = {'>','\r','\n'};
+static const WCHAR crlfW[] = {'\r','\n'};
 
 /* should be ordered as encoding names are sorted */
 typedef enum
@@ -146,6 +147,10 @@ typedef struct
     BOOL prop_changed;
     BOOL cdata;
 
+    BOOL text; /* last node was text node, so we shouldn't indent next node */
+    BOOL newline; /* newline was already added as a part of previous call */
+    UINT indent; /* indentation level for next node */
+
     BSTR version;
 
     BSTR encoding; /* exact property value */
@@ -454,14 +459,13 @@ static WCHAR *get_escaped_string(const WCHAR *str, escape_mode mode, int *len)
     return ret;
 }
 
-static void write_prolog_buffer(const mxwriter *This)
+static void write_prolog_buffer(mxwriter *This)
 {
     static const WCHAR versionW[] = {'<','?','x','m','l',' ','v','e','r','s','i','o','n','='};
     static const WCHAR encodingW[] = {' ','e','n','c','o','d','i','n','g','=','\"'};
     static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
     static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
     static const WCHAR noW[] = {'n','o','\"','?','>'};
-    static const WCHAR crlfW[] = {'\r','\n'};
 
     /* version */
     write_output_buffer(This->buffer, versionW, sizeof(versionW)/sizeof(WCHAR));
@@ -483,6 +487,7 @@ static void write_prolog_buffer(const mxwriter *This)
         write_output_buffer(This->buffer, noW, sizeof(noW)/sizeof(WCHAR));
 
     write_output_buffer(This->buffer, crlfW, sizeof(crlfW)/sizeof(WCHAR));
+    This->newline = TRUE;
 }
 
 /* Attempts to the write data from the mxwriter's buffer to
@@ -536,6 +541,41 @@ static void close_element_starttag(const mxwriter *This)
     write_output_buffer(This->buffer, gtW, 1);
 }
 
+static void write_node_indent(mxwriter *This)
+{
+    static const WCHAR tabW[] = {'\t'};
+    int indent = This->indent;
+
+    if (!This->props[MXWriter_Indent] || This->text)
+    {
+        This->text = FALSE;
+        return;
+    }
+
+    /* This is to workaround PI output logic that always puts newline chars,
+       document prolog PI does that too. */
+    if (!This->newline)
+        write_output_buffer(This->buffer, crlfW, sizeof(crlfW)/sizeof(WCHAR));
+    while (indent--)
+        write_output_buffer(This->buffer, tabW, 1);
+
+    This->newline = FALSE;
+    This->text = FALSE;
+}
+
+static inline void writer_inc_indent(mxwriter *This)
+{
+    This->indent++;
+}
+
+static inline void writer_dec_indent(mxwriter *This)
+{
+    if (This->indent) This->indent--;
+    /* depth is decreased only when element is closed, meaning it's not a text node
+       at this point */
+    This->text = FALSE;
+}
+
 static void set_element_name(mxwriter *This, const WCHAR *name, int len)
 {
     SysFreeString(This->element);
@@ -1082,8 +1122,11 @@ static HRESULT WINAPI SAXContentHandler_startElement(
     set_element_name(This, QName ? QName  : emptyW,
                            QName ? nQName : 0);
 
+    write_node_indent(This);
+
     write_output_buffer(This->buffer, ltW, 1);
     write_output_buffer(This->buffer, QName, nQName);
+    writer_inc_indent(This);
 
     if (attr)
     {
@@ -1147,6 +1190,8 @@ static HRESULT WINAPI SAXContentHandler_endElement(
          (nQName == -1 && This->class_version == MSXML6))
         return E_INVALIDARG;
 
+    writer_dec_indent(This);
+
     if (This->element)
     {
         static const WCHAR closeW[] = {'/','>'};
@@ -1157,6 +1202,7 @@ static HRESULT WINAPI SAXContentHandler_endElement(
         static const WCHAR closetagW[] = {'<','/'};
         static const WCHAR gtW[] = {'>'};
 
+        write_node_indent(This);
         write_output_buffer(This->buffer, closetagW, 2);
         write_output_buffer(This->buffer, QName, nQName);
         write_output_buffer(This->buffer, gtW, 1);
@@ -1181,6 +1227,9 @@ static HRESULT WINAPI SAXContentHandler_characters(
     close_element_starttag(This);
     set_element_name(This, NULL, 0);
 
+    if (!This->cdata)
+        This->text = TRUE;
+
     if (nchars)
     {
         if (This->cdata || This->props[MXWriter_DisableEscaping] == VARIANT_TRUE)
@@ -1230,6 +1279,7 @@ static HRESULT WINAPI SAXContentHandler_processingInstruction(
 
     if (!target) return E_INVALIDARG;
 
+    write_node_indent(This);
     write_output_buffer(This->buffer, openpiW, sizeof(openpiW)/sizeof(WCHAR));
 
     if (*target)
@@ -1242,6 +1292,7 @@ static HRESULT WINAPI SAXContentHandler_processingInstruction(
     }
 
     write_output_buffer(This->buffer, closepiW, sizeof(closepiW)/sizeof(WCHAR));
+    This->newline = TRUE;
 
     return S_OK;
 }
@@ -1381,6 +1432,7 @@ static HRESULT WINAPI SAXLexicalHandler_startCDATA(ISAXLexicalHandler *iface)
 
     TRACE("(%p)\n", This);
 
+    write_node_indent(This);
     write_output_buffer(This->buffer, scdataW, sizeof(scdataW)/sizeof(WCHAR));
     This->cdata = TRUE;
 
@@ -1411,6 +1463,7 @@ static HRESULT WINAPI SAXLexicalHandler_comment(ISAXLexicalHandler *iface, const
     if (!chars) return E_INVALIDARG;
 
     close_element_starttag(This);
+    write_node_indent(This);
 
     write_output_buffer(This->buffer, copenW, sizeof(copenW)/sizeof(WCHAR));
     if (nchars)
@@ -1609,6 +1662,9 @@ HRESULT MXWriter_create(MSXML_VERSION version, IUnknown *outer, void **ppObj)
 
     This->element = NULL;
     This->cdata = FALSE;
+    This->indent = 0;
+    This->text = FALSE;
+    This->newline = FALSE;
 
     This->dest = NULL;
     This->dest_written = 0;
diff --git a/dlls/msxml3/tests/saxreader.c b/dlls/msxml3/tests/saxreader.c
index dec62bb..1eff7f6 100644
--- a/dlls/msxml3/tests/saxreader.c
+++ b/dlls/msxml3/tests/saxreader.c
@@ -35,6 +35,8 @@
 
 #include "wine/test.h"
 
+static const WCHAR emptyW[] = {0};
+
 #define EXPECT_HR(hr,hr_exp) \
     ok(hr == hr_exp, "got 0x%08x, expected 0x%08x\n", hr, hr_exp)
 
@@ -2890,7 +2892,6 @@ static void test_mxwriter_default_properties(const struct mxwriter_props_t *tabl
 static void test_mxwriter_properties(void)
 {
     static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
-    static const WCHAR emptyW[] = {0};
     static const WCHAR testW[] = {'t','e','s','t',0};
     ISAXContentHandler *content;
     IMXWriter *writer;
@@ -3041,7 +3042,6 @@ static void test_mxwriter_properties(void)
 
 static void test_mxwriter_flush(void)
 {
-    static const WCHAR emptyW[] = {0};
     ISAXContentHandler *content;
     IMXWriter *writer;
     LARGE_INTEGER pos;
@@ -5245,6 +5245,63 @@ static void test_mxattr_localname(void)
     }
 }
 
+static void test_mxwriter_indent(void)
+{
+    ISAXContentHandler *content;
+    IMXWriter *writer;
+    VARIANT dest;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer);
+    ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
+
+    hr = IMXWriter_put_indent(writer, VARIANT_TRUE);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ISAXContentHandler_startDocument(content);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ISAXContentHandler_characters(content, _bstr_(""), 0);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("b"), -1, NULL);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("c"), -1, NULL);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ISAXContentHandler_endElement(content, emptyW, 0, emptyW, 0, _bstr_("c"), -1);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ISAXContentHandler_endElement(content, emptyW, 0, emptyW, 0, _bstr_("b"), -1);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ISAXContentHandler_endElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    hr = ISAXContentHandler_endDocument(content);
+    ok(hr == S_OK, "got %08x\n", hr);
+
+    V_VT(&dest) = VT_EMPTY;
+    hr = IMXWriter_get_output(writer, &dest);
+    ok(hr == S_OK, "got %08x\n", hr);
+    ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
+    ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n<a><b>\r\n\t\t<c/>\r\n\t</b>\r\n</a>"), V_BSTR(&dest)),
+        "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
+    VariantClear(&dest);
+
+    ISAXContentHandler_Release(content);
+    IMXWriter_Release(writer);
+
+    free_bstrs();
+}
+
 START_TEST(saxreader)
 {
     ISAXXMLReader *reader;
@@ -5292,6 +5349,7 @@ START_TEST(saxreader)
         test_mxwriter_stream();
         test_mxwriter_encoding();
         test_mxwriter_dispex();
+        test_mxwriter_indent();
     }
     else
         win_skip("MXXMLWriter not supported\n");




More information about the wine-cvs mailing list