xmllite/writer: Maintain a stack of written elements to write end tags.

Xmllite helps in a way that user doesn't have to specify closing tag
name (like it has to in case of MXWriter). So when closing current level
element qname is used for a closing tag; later this stack will also hold
namespace definitions that element is carrying to check if nested
element uses defined prefix.
This commit is contained in:
Nikolay Sivov 2014-06-14 22:46:09 +04:00 committed by Alexandre Julliard
parent 45b9027258
commit af46bab594
2 changed files with 161 additions and 28 deletions

View File

@ -583,10 +583,6 @@ static void test_writestartelement(void)
hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes); hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr); ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
/* write another element without closing previous one */
hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
hr = IXmlWriter_WriteStartElement(writer, NULL, NULL, NULL); hr = IXmlWriter_WriteStartElement(writer, NULL, NULL, NULL);
ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
@ -629,6 +625,51 @@ static void test_writestartelement(void)
IXmlWriter_Release(writer); IXmlWriter_Release(writer);
} }
static void test_writeendelement(void)
{
static const WCHAR aW[] = {'a',0};
static const WCHAR bW[] = {'b',0};
char *ptr;
IXmlWriter *writer;
IStream *stream;
HGLOBAL hglobal;
HRESULT hr;
hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
ok(hr == S_OK, "got 0x%08x\n", hr);
hr = pCreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
hr = IXmlWriter_SetOutput(writer, (IUnknown*)stream);
ok(hr == S_OK, "got 0x%08x\n", hr);
hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
ok(hr == S_OK, "got 0x%08x\n", hr);
hr = IXmlWriter_WriteStartElement(writer, NULL, bW, NULL);
ok(hr == S_OK, "got 0x%08x\n", hr);
hr = IXmlWriter_WriteEndElement(writer);
ok(hr == S_OK, "got 0x%08x\n", hr);
hr = IXmlWriter_WriteEndElement(writer);
ok(hr == S_OK, "got 0x%08x\n", hr);
hr = GetHGlobalFromStream(stream, &hglobal);
ok(hr == S_OK, "got 0x%08x\n", hr);
hr = IXmlWriter_Flush(writer);
ok(hr == S_OK, "got 0x%08x\n", hr);
ptr = GlobalLock(hglobal);
ok(!strncmp(ptr, "<a><b /></a>", 12), "got %s\n", ptr);
GlobalUnlock(hglobal);
IXmlWriter_Release(writer);
IStream_Release(stream);
}
START_TEST(writer) START_TEST(writer)
{ {
if (!init_pointers()) if (!init_pointers())
@ -638,6 +679,7 @@ START_TEST(writer)
test_writeroutput(); test_writeroutput();
test_writestartdocument(); test_writestartdocument();
test_writestartelement(); test_writestartelement();
test_writeendelement();
test_flush(); test_flush();
test_omitxmldeclaration(); test_omitxmldeclaration();
test_bom(); test_bom();

View File

@ -29,6 +29,7 @@
#include "initguid.h" #include "initguid.h"
#include "wine/debug.h" #include "wine/debug.h"
#include "wine/list.h"
#include "wine/unicode.h" #include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(xmllite); WINE_DEFAULT_DEBUG_CHANNEL(xmllite);
@ -36,6 +37,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(xmllite);
/* not defined in public headers */ /* not defined in public headers */
DEFINE_GUID(IID_IXmlWriterOutput, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, 0x33, 0x24, 0x51, 0xbc, 0x1a); DEFINE_GUID(IID_IXmlWriterOutput, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, 0x33, 0x24, 0x51, 0xbc, 0x1a);
#define ARRAY_SIZE(array) (sizeof(array)/sizeof((array)[0]))
static const WCHAR closeelementW[] = {'<','/'};
static const WCHAR closepiW[] = {'?','>'}; static const WCHAR closepiW[] = {'?','>'};
static const WCHAR ltW[] = {'<'}; static const WCHAR ltW[] = {'<'};
static const WCHAR gtW[] = {'>'}; static const WCHAR gtW[] = {'>'};
@ -71,6 +75,13 @@ typedef struct
static const struct IUnknownVtbl xmlwriteroutputvtbl; static const struct IUnknownVtbl xmlwriteroutputvtbl;
struct element
{
struct list entry;
WCHAR *qname;
unsigned int len; /* qname length in chars */
};
typedef struct _xmlwriter typedef struct _xmlwriter
{ {
IXmlWriter IXmlWriter_iface; IXmlWriter IXmlWriter_iface;
@ -84,6 +95,7 @@ typedef struct _xmlwriter
XmlWriterState state; XmlWriterState state;
BOOL bomwritten; BOOL bomwritten;
BOOL starttagopen; BOOL starttagopen;
struct list elements;
} xmlwriter; } xmlwriter;
static inline xmlwriter *impl_from_IXmlWriter(IXmlWriter *iface) static inline xmlwriter *impl_from_IXmlWriter(IXmlWriter *iface)
@ -140,6 +152,52 @@ static inline void writer_free(xmlwriter *writer, void *mem)
m_free(writer->imalloc, mem); m_free(writer->imalloc, mem);
} }
static struct element *alloc_element(xmlwriter *writer, const WCHAR *prefix, const WCHAR *local)
{
struct element *ret;
int len;
ret = writer_alloc(writer, sizeof(*ret));
if (!ret) return ret;
len = prefix ? strlenW(prefix) + 1 /* ':' */ : 0;
len += strlenW(local);
ret->qname = writer_alloc(writer, (len + 1)*sizeof(WCHAR));
ret->len = len;
if (prefix) {
static const WCHAR colonW[] = {':',0};
strcpyW(ret->qname, prefix);
strcatW(ret->qname, colonW);
}
else
ret->qname[0] = 0;
strcatW(ret->qname, local);
return ret;
}
static void free_element(xmlwriter *writer, struct element *element)
{
writer_free(writer, element->qname);
writer_free(writer, element);
}
static void push_element(xmlwriter *writer, struct element *element)
{
list_add_head(&writer->elements, &element->entry);
}
static struct element *pop_element(xmlwriter *writer)
{
struct element *element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
if (element)
list_remove(&element->entry);
return element;
}
static HRESULT init_output_buffer(xmlwriteroutput *output) static HRESULT init_output_buffer(xmlwriteroutput *output)
{ {
struct output_buffer *buffer = &output->buffer; struct output_buffer *buffer = &output->buffer;
@ -223,10 +281,10 @@ static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, i
static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len) static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len)
{ {
static const WCHAR quotW[] = {'"'}; static const WCHAR quoteW[] = {'"'};
write_output_buffer(output, quotW, 1); write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
write_output_buffer(output, data, len); write_output_buffer(output, data, len);
write_output_buffer(output, quotW, 1); write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
return S_OK; return S_OK;
} }
@ -236,7 +294,7 @@ static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix,
if (prefix) { if (prefix) {
static const WCHAR colW[] = {':'}; static const WCHAR colW[] = {':'};
write_output_buffer(output, prefix, -1); write_output_buffer(output, prefix, -1);
write_output_buffer(output, colW, 1); write_output_buffer(output, colW, ARRAY_SIZE(colW));
} }
write_output_buffer(output, local_name, -1); write_output_buffer(output, local_name, -1);
@ -317,7 +375,7 @@ static HRESULT writer_close_starttag(xmlwriter *writer)
HRESULT hr; HRESULT hr;
if (!writer->starttagopen) return S_OK; if (!writer->starttagopen) return S_OK;
hr = write_output_buffer(writer->output, gtW, 1); hr = write_output_buffer(writer->output, gtW, ARRAY_SIZE(gtW));
writer->starttagopen = FALSE; writer->starttagopen = FALSE;
writer->state = XmlWriterState_Content; writer->state = XmlWriterState_Content;
return hr; return hr;
@ -356,10 +414,18 @@ static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface)
ref = InterlockedDecrement(&This->ref); ref = InterlockedDecrement(&This->ref);
if (ref == 0) { if (ref == 0) {
struct element *element, *element2;
IMalloc *imalloc = This->imalloc; IMalloc *imalloc = This->imalloc;
IXmlWriter_Flush(iface); IXmlWriter_Flush(iface);
if (This->output) IUnknown_Release(&This->output->IXmlWriterOutput_iface); if (This->output) IUnknown_Release(&This->output->IXmlWriterOutput_iface);
/* element stack */
LIST_FOR_EACH_ENTRY_SAFE(element, element2, &This->elements, struct element, entry) {
list_remove(&element->entry);
free_element(This, element);
}
writer_free(This, This); writer_free(This, This);
if (imalloc) IMalloc_Release(imalloc); if (imalloc) IMalloc_Release(imalloc);
} }
@ -531,7 +597,6 @@ static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR pr
LPCWSTR local_name, LPCWSTR uri, LPCWSTR value) LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
{ {
xmlwriter *This = impl_from_IXmlWriter(iface); xmlwriter *This = impl_from_IXmlWriter(iface);
static const WCHAR closeW[] = {'<','/'};
TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), TRACE("(%p)->(%s %s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name),
wine_dbgstr_w(uri), wine_dbgstr_w(value)); wine_dbgstr_w(uri), wine_dbgstr_w(value));
@ -548,16 +613,16 @@ static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR pr
} }
write_encoding_bom(This); write_encoding_bom(This);
write_output_buffer(This->output, ltW, 1); write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW));
write_output_qname(This->output, prefix, local_name); write_output_qname(This->output, prefix, local_name);
write_output_buffer(This->output, gtW, 1); write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
if (value) if (value)
write_output_buffer(This->output, value, -1); write_output_buffer(This->output, value, -1);
write_output_buffer(This->output, closeW, 2); write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
write_output_qname(This->output, prefix, local_name); write_output_qname(This->output, prefix, local_name);
write_output_buffer(This->output, gtW, 1); write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
This->state = XmlWriterState_Content; This->state = XmlWriterState_Content;
return S_OK; return S_OK;
@ -575,10 +640,27 @@ static HRESULT WINAPI xmlwriter_WriteEndDocument(IXmlWriter *iface)
static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface) static HRESULT WINAPI xmlwriter_WriteEndElement(IXmlWriter *iface)
{ {
xmlwriter *This = impl_from_IXmlWriter(iface); xmlwriter *This = impl_from_IXmlWriter(iface);
struct element *element;
FIXME("%p\n", This); TRACE("%p\n", This);
return E_NOTIMPL; element = pop_element(This);
if (!element)
return WR_E_INVALIDACTION;
if (This->starttagopen) {
static WCHAR closetagW[] = {' ','/','>'};
write_output_buffer(This->output, closetagW, ARRAY_SIZE(closetagW));
This->starttagopen = FALSE;
}
else {
/* write full end tag */
write_output_buffer(This->output, closeelementW, ARRAY_SIZE(closeelementW));
write_output_buffer(This->output, element->qname, element->len);
write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
}
return S_OK;
} }
static HRESULT WINAPI xmlwriter_WriteEntityRef(IXmlWriter *iface, LPCWSTR pwszName) static HRESULT WINAPI xmlwriter_WriteEntityRef(IXmlWriter *iface, LPCWSTR pwszName)
@ -662,11 +744,11 @@ static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LP
} }
write_encoding_bom(This); write_encoding_bom(This);
write_output_buffer(This->output, openpiW, sizeof(openpiW)/sizeof(WCHAR)); write_output_buffer(This->output, openpiW, ARRAY_SIZE(openpiW));
write_output_buffer(This->output, name, -1); write_output_buffer(This->output, name, -1);
write_output_buffer(This->output, spaceW, 1); write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
write_output_buffer(This->output, text, -1); write_output_buffer(This->output, text, -1);
write_output_buffer(This->output, closepiW, sizeof(closepiW)/sizeof(WCHAR)); write_output_buffer(This->output, closepiW, ARRAY_SIZE(closepiW));
if (!strcmpW(name, xmlW)) if (!strcmpW(name, xmlW))
This->state = XmlWriterState_PIDocStarted; This->state = XmlWriterState_PIDocStarted;
@ -729,25 +811,25 @@ static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandal
if (This->omitxmldecl) return S_OK; if (This->omitxmldecl) return S_OK;
/* version */ /* version */
write_output_buffer(This->output, versionW, sizeof(versionW)/sizeof(WCHAR)); write_output_buffer(This->output, versionW, ARRAY_SIZE(versionW));
/* encoding */ /* encoding */
write_output_buffer(This->output, encodingW, sizeof(encodingW)/sizeof(WCHAR)); write_output_buffer(This->output, encodingW, ARRAY_SIZE(encodingW));
write_output_buffer_quoted(This->output, get_encoding_name(This->output->encoding), -1); write_output_buffer_quoted(This->output, get_encoding_name(This->output->encoding), -1);
/* standalone */ /* standalone */
if (standalone == XmlStandalone_Omit) if (standalone == XmlStandalone_Omit)
write_output_buffer(This->output, closepiW, sizeof(closepiW)/sizeof(WCHAR)); write_output_buffer(This->output, closepiW, ARRAY_SIZE(closepiW));
else { else {
static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'}; static const WCHAR standaloneW[] = {' ','s','t','a','n','d','a','l','o','n','e','=','\"'};
static const WCHAR yesW[] = {'y','e','s','\"','?','>'}; static const WCHAR yesW[] = {'y','e','s','\"','?','>'};
static const WCHAR noW[] = {'n','o','\"','?','>'}; static const WCHAR noW[] = {'n','o','\"','?','>'};
write_output_buffer(This->output, standaloneW, sizeof(standaloneW)/sizeof(WCHAR)); write_output_buffer(This->output, standaloneW, ARRAY_SIZE(standaloneW));
if (standalone == XmlStandalone_Yes) if (standalone == XmlStandalone_Yes)
write_output_buffer(This->output, yesW, sizeof(yesW)/sizeof(WCHAR)); write_output_buffer(This->output, yesW, ARRAY_SIZE(yesW));
else else
write_output_buffer(This->output, noW, sizeof(noW)/sizeof(WCHAR)); write_output_buffer(This->output, noW, ARRAY_SIZE(noW));
} }
return S_OK; return S_OK;
@ -756,6 +838,7 @@ static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandal
static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri) static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR prefix, LPCWSTR local_name, LPCWSTR uri)
{ {
xmlwriter *This = impl_from_IXmlWriter(iface); xmlwriter *This = impl_from_IXmlWriter(iface);
struct element *element;
TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri)); TRACE("(%p)->(%s %s %s)\n", This, wine_dbgstr_w(prefix), wine_dbgstr_w(local_name), wine_dbgstr_w(uri));
@ -765,14 +848,21 @@ static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR pre
if (!local_name) if (!local_name)
return E_INVALIDARG; return E_INVALIDARG;
if (This->state == XmlWriterState_ElemStarted) /* close pending element */
return WR_E_INVALIDACTION; if (This->starttagopen)
write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
element = alloc_element(This, prefix, local_name);
if (!element)
return E_OUTOFMEMORY;
write_encoding_bom(This); write_encoding_bom(This);
This->state = XmlWriterState_ElemStarted; This->state = XmlWriterState_ElemStarted;
This->starttagopen = TRUE; This->starttagopen = TRUE;
write_output_buffer(This->output, ltW, 1); push_element(This, element);
write_output_buffer(This->output, ltW, ARRAY_SIZE(ltW));
write_output_qname(This->output, prefix, local_name); write_output_qname(This->output, prefix, local_name);
return S_OK; return S_OK;
@ -939,6 +1029,7 @@ HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc)
writer->state = XmlWriterState_Initial; writer->state = XmlWriterState_Initial;
writer->bomwritten = FALSE; writer->bomwritten = FALSE;
writer->starttagopen = FALSE; writer->starttagopen = FALSE;
list_init(&writer->elements);
*obj = &writer->IXmlWriter_iface; *obj = &writer->IXmlWriter_iface;