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);
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);
ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
@ -629,6 +625,51 @@ static void test_writestartelement(void)
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)
{
if (!init_pointers())
@ -638,6 +679,7 @@ START_TEST(writer)
test_writeroutput();
test_writestartdocument();
test_writestartelement();
test_writeendelement();
test_flush();
test_omitxmldeclaration();
test_bom();

View File

@ -29,6 +29,7 @@
#include "initguid.h"
#include "wine/debug.h"
#include "wine/list.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(xmllite);
@ -36,6 +37,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(xmllite);
/* not defined in public headers */
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 ltW[] = {'<'};
static const WCHAR gtW[] = {'>'};
@ -71,6 +75,13 @@ typedef struct
static const struct IUnknownVtbl xmlwriteroutputvtbl;
struct element
{
struct list entry;
WCHAR *qname;
unsigned int len; /* qname length in chars */
};
typedef struct _xmlwriter
{
IXmlWriter IXmlWriter_iface;
@ -84,6 +95,7 @@ typedef struct _xmlwriter
XmlWriterState state;
BOOL bomwritten;
BOOL starttagopen;
struct list elements;
} xmlwriter;
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);
}
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)
{
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 const WCHAR quotW[] = {'"'};
write_output_buffer(output, quotW, 1);
static const WCHAR quoteW[] = {'"'};
write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
write_output_buffer(output, data, len);
write_output_buffer(output, quotW, 1);
write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
return S_OK;
}
@ -236,7 +294,7 @@ static HRESULT write_output_qname(xmlwriteroutput *output, const WCHAR *prefix,
if (prefix) {
static const WCHAR colW[] = {':'};
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);
@ -317,7 +375,7 @@ static HRESULT writer_close_starttag(xmlwriter *writer)
HRESULT hr;
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->state = XmlWriterState_Content;
return hr;
@ -356,10 +414,18 @@ static ULONG WINAPI xmlwriter_Release(IXmlWriter *iface)
ref = InterlockedDecrement(&This->ref);
if (ref == 0) {
struct element *element, *element2;
IMalloc *imalloc = This->imalloc;
IXmlWriter_Flush(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);
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)
{
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),
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_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_buffer(This->output, gtW, 1);
write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
if (value)
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_buffer(This->output, gtW, 1);
write_output_buffer(This->output, gtW, ARRAY_SIZE(gtW));
This->state = XmlWriterState_Content;
return S_OK;
@ -575,10 +640,27 @@ static HRESULT WINAPI xmlwriter_WriteEndDocument(IXmlWriter *iface)
static HRESULT WINAPI xmlwriter_WriteEndElement(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)
@ -662,11 +744,11 @@ static HRESULT WINAPI xmlwriter_WriteProcessingInstruction(IXmlWriter *iface, LP
}
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, spaceW, 1);
write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
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))
This->state = XmlWriterState_PIDocStarted;
@ -729,25 +811,25 @@ static HRESULT WINAPI xmlwriter_WriteStartDocument(IXmlWriter *iface, XmlStandal
if (This->omitxmldecl) return S_OK;
/* version */
write_output_buffer(This->output, versionW, sizeof(versionW)/sizeof(WCHAR));
write_output_buffer(This->output, versionW, ARRAY_SIZE(versionW));
/* 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);
/* standalone */
if (standalone == XmlStandalone_Omit)
write_output_buffer(This->output, closepiW, sizeof(closepiW)/sizeof(WCHAR));
write_output_buffer(This->output, closepiW, ARRAY_SIZE(closepiW));
else {
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','\"','?','>'};
write_output_buffer(This->output, standaloneW, sizeof(standaloneW)/sizeof(WCHAR));
write_output_buffer(This->output, standaloneW, ARRAY_SIZE(standaloneW));
if (standalone == XmlStandalone_Yes)
write_output_buffer(This->output, yesW, sizeof(yesW)/sizeof(WCHAR));
write_output_buffer(This->output, yesW, ARRAY_SIZE(yesW));
else
write_output_buffer(This->output, noW, sizeof(noW)/sizeof(WCHAR));
write_output_buffer(This->output, noW, ARRAY_SIZE(noW));
}
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)
{
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));
@ -765,14 +848,21 @@ static HRESULT WINAPI xmlwriter_WriteStartElement(IXmlWriter *iface, LPCWSTR pre
if (!local_name)
return E_INVALIDARG;
if (This->state == XmlWriterState_ElemStarted)
return WR_E_INVALIDACTION;
/* close pending element */
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);
This->state = XmlWriterState_ElemStarted;
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);
return S_OK;
@ -939,6 +1029,7 @@ HRESULT WINAPI CreateXmlWriter(REFIID riid, void **obj, IMalloc *imalloc)
writer->state = XmlWriterState_Initial;
writer->bomwritten = FALSE;
writer->starttagopen = FALSE;
list_init(&writer->elements);
*obj = &writer->IXmlWriter_iface;