xmllite/writer: Improve namespace support in WriteAttributeString().

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Nikolay Sivov 2018-09-12 16:25:52 +03:00 committed by Alexandre Julliard
parent 9730971e8e
commit 1517695f78
2 changed files with 225 additions and 95 deletions

View File

@ -1604,28 +1604,54 @@ static void test_WriteAttributeString(void)
const char *output;
const char *output_partial;
HRESULT hr;
int todo;
int todo_partial;
int todo_hr;
}
attribute_tests[] =
{
{ NULL, "a", NULL, "b", "<e a=\"b\" />", "<e a=\"b\"" },
{ "", "a", NULL, "b", "<e a=\"b\" />", "<e a=\"b\"" },
{ NULL, "a", "", "b", "<e a=\"b\" />", "<e a=\"b\"" },
{ "", "a", "", "b", "<e a=\"b\" />", "<e a=\"b\"" },
{ "prefix", "local", "uri", "b", "<e prefix:local=\"b\" xmlns:prefix=\"uri\" />", "<e prefix:local=\"b\"" },
{ NULL, "a", "http://www.w3.org/2000/xmlns/", "defuri", "<e xmlns:a=\"defuri\" />", "<e xmlns:a=\"defuri\"" },
{ "xmlns", "a", NULL, "uri", "<e xmlns:a=\"uri\" />", "<e xmlns:a=\"uri\"" },
{ "xmlns", "a", "", "uri", "<e xmlns:a=\"uri\" />", "<e xmlns:a=\"uri\"" },
{ "prefix", "xmlns", "uri", "value", "<e prefix:xmlns=\"value\" xmlns:prefix=\"uri\" />", "<e prefix:xmlns=\"value\"" },
{ "prefix", "xmlns", "uri", NULL, "<e prefix:xmlns=\"\" xmlns:prefix=\"uri\" />", "<e prefix:xmlns=\"\"" },
{ "prefix", "xmlns", "uri", "", "<e prefix:xmlns=\"\" xmlns:prefix=\"uri\" />", "<e prefix:xmlns=\"\"" },
{ "prefix", "xmlns", NULL, "uri", "<e xmlns=\"uri\" />", "<e xmlns=\"uri\"" },
{ "prefix", "xmlns", "", "uri", "<e xmlns=\"uri\" />", "<e xmlns=\"uri\"" },
/* Autogenerated prefix names. */
{ NULL, "a", "defuri", NULL, "<e p1:a=\"\" xmlns:p1=\"defuri\" />", "<e p1:a=\"\"" },
{ NULL, "a", "defuri", "b", "<e p1:a=\"b\" xmlns:p1=\"defuri\" />", "<e p1:a=\"b\"" },
{ NULL, "a", "defuri", NULL, "<e p1:a=\"\" xmlns:p1=\"defuri\" />", "<e p1:a=\"\"", S_OK, 1, 1, 1 },
{ NULL, "a", "defuri", "b", "<e p1:a=\"b\" xmlns:p1=\"defuri\" />", "<e p1:a=\"b\"", S_OK, 1, 1, 1 },
{ "", "a", "defuri", NULL, "<e p1:a=\"\" xmlns:p1=\"defuri\" />", "<e p1:a=\"\"", S_OK, 1, 1, 1 },
{ NULL, "a", "defuri", "", "<e p1:a=\"\" xmlns:p1=\"defuri\" />", "<e p1:a=\"\"", S_OK, 1, 1, 1 },
{ "", "a", "defuri", "b", "<e p1:a=\"b\" xmlns:p1=\"defuri\" />", "<e p1:a=\"b\"", S_OK, 1, 1, 1 },
/* Failing cases. */
{ NULL, NULL, "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", E_INVALIDARG },
{ "", "a", "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", WR_E_XMLNSPREFIXDECLARATION, 1, 1, 1 },
{ "", NULL, "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", E_INVALIDARG },
{ "", "", "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", E_INVALIDARG, 1, 1, 1 },
{ NULL, "", "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", E_INVALIDARG, 1, 1, 1 },
{ "prefix", "a", "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", WR_E_XMLNSURIDECLARATION, 1, 1, 1 },
{ "prefix", NULL, "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", E_INVALIDARG },
{ "prefix", NULL, NULL, "b", "<e />", "<e", E_INVALIDARG },
{ "prefix", NULL, "uri", NULL, "<e />", "<e", E_INVALIDARG },
{ "xmlns", NULL, NULL, "uri", "<e />", "<e", WR_E_NSPREFIXDECLARED },
{ "xmlns", "a", "defuri", NULL, "<e />", "<e", WR_E_XMLNSPREFIXDECLARATION },
{ NULL, "xmlns", "uri", NULL, "<e />", "<e", WR_E_XMLNSPREFIXDECLARATION },
{ "xmlns", NULL, "uri", NULL, "<e />", "<e", WR_E_XMLNSPREFIXDECLARATION },
{ "prefix", "a", "http://www.w3.org/2000/xmlns/", "defuri", "<e />", "<e", WR_E_XMLNSURIDECLARATION },
{ "xmlns", "a", "b", "uri", "<e />", "<e", WR_E_XMLNSPREFIXDECLARATION },
{ NULL, "xmlns", "uri", NULL, "<e />", "<e", WR_E_XMLNSPREFIXDECLARATION, 0, 0, 1 },
{ "xmlns", NULL, "uri", NULL, "<e />", "<e", WR_E_XMLNSPREFIXDECLARATION, 0, 0, 1 },
{ "pre:fix", "local", "uri", "b", "<e />", "<e", WC_E_NAMECHARACTER },
{ "pre:fix", NULL, "uri", "b", "<e />", "<e", E_INVALIDARG },
{ "prefix", "lo:cal", "uri", "b", "<e />", "<e", WC_E_NAMECHARACTER },
{ "xmlns", NULL, NULL, "uri", "<e />", "<e", WR_E_NSPREFIXDECLARED },
{ "xmlns", NULL, "", "uri", "<e />", "<e", WR_E_NSPREFIXDECLARED },
{ "xmlns", "", NULL, "uri", "<e />", "<e", WR_E_NSPREFIXDECLARED },
{ "xmlns", "", "", "uri", "<e />", "<e", WR_E_NSPREFIXDECLARED },
};
IXmlWriter *writer;
@ -1650,13 +1676,13 @@ static void test_WriteAttributeString(void)
hr = write_attribute_string(writer, attribute_tests[i].prefix, attribute_tests[i].local,
attribute_tests[i].uri, attribute_tests[i].value);
todo_wine_if(i != 0)
ok(hr == attribute_tests[i].hr, "%u: unexpected hr %#x.\n", i, hr);
todo_wine_if(attribute_tests[i].todo_hr)
ok(hr == attribute_tests[i].hr, "%u: unexpected hr %#x, expected %#x.\n", i, hr, attribute_tests[i].hr);
hr = IXmlWriter_Flush(writer);
ok(hr == S_OK, "Failed to flush, hr %#x.\n", hr);
check_output(stream, attribute_tests[i].output_partial, i == 1 || i == 2 || i == 3 || i == 4, __LINE__);
check_output(stream, attribute_tests[i].output_partial, attribute_tests[i].todo_partial, __LINE__);
hr = IXmlWriter_WriteEndDocument(writer);
ok(hr == S_OK, "Failed to end document, hr %#x.\n", hr);
@ -1664,11 +1690,11 @@ static void test_WriteAttributeString(void)
hr = IXmlWriter_Flush(writer);
ok(hr == S_OK, "Failed to flush, hr %#x.\n", hr);
check_output(stream, attribute_tests[i].output, i == 1 || i == 2 || i == 3 || i == 4, __LINE__);
check_output(stream, attribute_tests[i].output, attribute_tests[i].todo, __LINE__);
IStream_Release(stream);
}
/* with namespaces */
/* With namespaces */
stream = writer_set_output(writer);
hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
@ -1678,12 +1704,14 @@ static void test_WriteAttributeString(void)
ok(hr == S_OK, "got 0x%08x\n", hr);
hr = write_attribute_string(writer, "prefix", "local", "uri", "b");
todo_wine
ok(hr == S_OK, "got 0x%08x\n", hr);
hr = write_attribute_string(writer, NULL, "a", NULL, "b");
ok(hr == S_OK, "got 0x%08x\n", hr);
hr = write_attribute_string(writer, "xmlns", "prefix", NULL, "uri");
ok(hr == S_OK, "got 0x%08x\n", hr);
hr = write_attribute_string(writer, "p", "attr", NULL, "value");
ok(hr == S_OK, "Failed to write attribute string, hr %#x.\n", hr);
@ -1691,6 +1719,15 @@ todo_wine
todo_wine
ok(hr == WR_E_DUPLICATEATTRIBUTE, "got 0x%08x\n", hr);
hr = write_start_element(writer, NULL, "b", NULL);
ok(hr == S_OK, "got 0x%08x\n", hr);
hr = write_attribute_string(writer, NULL, "attr2", "outeruri", "value");
ok(hr == S_OK, "Failed to write attribute string, hr %#x.\n", hr);
hr = write_attribute_string(writer, "pr", "attr3", "outeruri", "value");
ok(hr == S_OK, "Failed to write attribute string, hr %#x.\n", hr);
hr = IXmlWriter_WriteEndDocument(writer);
ok(hr == S_OK, "got 0x%08x\n", hr);
@ -1698,10 +1735,12 @@ todo_wine
ok(hr == S_OK, "got 0x%08x\n", hr);
CHECK_OUTPUT_TODO(stream,
"<p:a prefix:local=\"b\" a=\"b\" p:attr=\"value\" xmlns:prefix=\"uri\" xmlns:p=\"outeruri\" />");
"<p:a prefix:local=\"b\" a=\"b\" xmlns:prefix=\"uri\" p:attr=\"value\" xmlns:p=\"outeruri\">"
"<b p:attr2=\"value\" pr:attr3=\"value\" xmlns:pr=\"outeruri\" />"
"</p:a>");
IXmlWriter_Release(writer);
IStream_Release(stream);
IXmlWriter_Release(writer);
}
static void test_WriteFullEndElement(void)

View File

@ -2,7 +2,7 @@
* IXmlWriter implementation
*
* Copyright 2011 Alistair Leslie-Hughes
* Copyright 2014, 2016 Nikolay Sivov for CodeWeavers
* Copyright 2014-2018 Nikolay Sivov for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -84,14 +84,6 @@ typedef struct
static const struct IUnknownVtbl xmlwriteroutputvtbl;
struct ns
{
struct list entry;
WCHAR *prefix;
int prefix_len;
WCHAR *uri;
};
struct element
{
struct list entry;
@ -100,6 +92,16 @@ struct element
struct list ns;
};
struct ns
{
struct list entry;
WCHAR *prefix;
int prefix_len;
WCHAR *uri;
BOOL emitted;
struct element *element;
};
typedef struct _xmlwriter
{
IXmlWriter IXmlWriter_iface;
@ -261,22 +263,102 @@ static WCHAR *writer_strdupW(const xmlwriter *writer, const WCHAR *str)
return writer_strndupW(writer, str, -1);
}
static void writer_push_ns(xmlwriter *writer, const WCHAR *prefix, int prefix_len, const WCHAR *uri)
static struct ns *writer_push_ns(xmlwriter *writer, const WCHAR *prefix, int prefix_len, const WCHAR *uri)
{
struct element *element;
struct ns *ns;
element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
if (!element)
return;
return NULL;
if ((ns = writer_alloc(writer, sizeof(*ns))))
{
ns->prefix = writer_strndupW(writer, prefix, prefix_len);
ns->prefix_len = prefix_len;
ns->uri = writer_strdupW(writer, uri);
ns->emitted = FALSE;
ns->element = element;
list_add_tail(&element->ns, &ns->entry);
}
return ns;
}
static BOOL is_empty_string(const WCHAR *str)
{
return !str || !*str;
}
static struct ns *writer_find_ns_current(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
{
struct element *element;
struct ns *ns;
if (is_empty_string(prefix) || is_empty_string(uri))
return NULL;
element = LIST_ENTRY(list_head(&writer->elements), struct element, entry);
LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
{
if (!strcmpW(uri, ns->uri) && !strcmpW(prefix, ns->prefix))
return ns;
}
return NULL;
}
static struct ns *writer_find_ns(const xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
{
struct element *element;
struct ns *ns;
if (is_empty_string(prefix) && is_empty_string(uri))
return NULL;
LIST_FOR_EACH_ENTRY(element, &writer->elements, struct element, entry)
{
LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
{
if (!uri)
{
if (!ns->prefix) continue;
if (!strcmpW(ns->prefix, prefix))
return ns;
}
else if (!strcmpW(uri, ns->uri))
{
if (prefix && !*prefix)
return NULL;
if (!prefix || !strcmpW(prefix, ns->prefix))
return ns;
}
}
}
return NULL;
}
static HRESULT is_valid_ncname(const WCHAR *str, int *out)
{
int len = 0;
*out = 0;
if (!str || !*str)
return S_OK;
while (*str)
{
if (!is_ncnamechar(*str))
return WC_E_NAMECHARACTER;
len++;
str++;
}
*out = len;
return S_OK;
}
static HRESULT init_output_buffer(xmlwriteroutput *output)
@ -363,6 +445,7 @@ static HRESULT write_output_buffer(xmlwriteroutput *output, const WCHAR *data, i
static HRESULT write_output_buffer_quoted(xmlwriteroutput *output, const WCHAR *data, int len)
{
write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
if (!is_empty_string(data))
write_output_buffer(output, data, len);
write_output_buffer(output, quoteW, ARRAY_SIZE(quoteW));
return S_OK;
@ -503,6 +586,9 @@ static void writer_output_ns(xmlwriter *writer, struct element *element)
LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
{
if (ns->emitted)
continue;
write_output_qname(writer->output, xmlnsW, ARRAY_SIZE(xmlnsW), ns->prefix, ns->prefix_len);
write_output_buffer(writer->output, eqW, ARRAY_SIZE(eqW));
write_output_buffer_quoted(writer->output, ns->uri, -1);
@ -718,13 +804,26 @@ static HRESULT WINAPI xmlwriter_WriteAttributes(IXmlWriter *iface, IXmlReader *p
return E_NOTIMPL;
}
static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR ns_prefix,
LPCWSTR local_name, LPCWSTR ns_uri, LPCWSTR value)
static void write_output_attribute(xmlwriter *writer, const WCHAR *prefix, int prefix_len,
const WCHAR *local, int local_len, const WCHAR *value)
{
xmlwriter *This = impl_from_IXmlWriter(iface);
write_output_buffer(writer->output, spaceW, ARRAY_SIZE(spaceW));
write_output_qname(writer->output, prefix, prefix_len, local, local_len);
write_output_buffer(writer->output, eqW, ARRAY_SIZE(eqW));
write_output_buffer_quoted(writer->output, value, -1);
}
TRACE("%p %s %s %s %s\n", This, debugstr_w(ns_prefix), debugstr_w(local_name),
debugstr_w(ns_uri), debugstr_w(value));
static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR prefix,
LPCWSTR local, LPCWSTR uri, LPCWSTR value)
{
static const WCHAR xmlnsW[] = {'x','m','l','n','s',0};
xmlwriter *This = impl_from_IXmlWriter(iface);
int prefix_len, local_len;
BOOL is_xmlns_prefix;
struct ns *ns;
HRESULT hr;
TRACE("%p %s %s %s %s\n", This, debugstr_w(prefix), debugstr_w(local), debugstr_w(uri), debugstr_w(value));
switch (This->state)
{
@ -740,16 +839,65 @@ static HRESULT WINAPI xmlwriter_WriteAttributeString(IXmlWriter *iface, LPCWSTR
;
}
if (ns_prefix || ns_uri)
/* Prefix "xmlns" */
is_xmlns_prefix = prefix && !strcmpW(prefix, xmlnsW);
if (is_xmlns_prefix && is_empty_string(uri) && is_empty_string(local))
return WR_E_NSPREFIXDECLARED;
if (!local)
return E_INVALIDARG;
/* Validate prefix and local name */
if (FAILED(hr = is_valid_ncname(prefix, &prefix_len)))
return hr;
if (FAILED(hr = is_valid_ncname(local, &local_len)))
return hr;
/* Trivial case, no prefix. */
if (prefix_len == 0 && is_empty_string(uri))
{
FIXME("namespaces are not supported.\n");
return E_NOTIMPL;
write_output_attribute(This, prefix, prefix_len, local, local_len, value);
return S_OK;
}
write_output_buffer(This->output, spaceW, ARRAY_SIZE(spaceW));
write_output_buffer(This->output, local_name, -1);
write_output_buffer(This->output, eqW, ARRAY_SIZE(eqW));
write_output_buffer_quoted(This->output, value, -1);
if (is_xmlns_prefix || (prefix_len == 0 && uri && !strcmpW(uri, xmlnsuriW)))
{
if (prefix_len && !is_empty_string(uri))
return WR_E_XMLNSPREFIXDECLARATION;
/* Look for exact match defined in current element, and write it out. */
if (!(ns = writer_find_ns_current(This, prefix, value)))
ns = writer_push_ns(This, local, local_len, value);
ns->emitted = TRUE;
write_output_attribute(This, xmlnsW, ARRAY_SIZE(xmlnsW) - 1, local, local_len, value);
return S_OK;
}
/* Ignore prefix is URI wasn't specified. */
if (is_empty_string(uri))
{
write_output_attribute(This, NULL, 0, local, local_len, value);
return S_OK;
}
if (!(ns = writer_find_ns(This, prefix, uri)))
{
if (is_empty_string(prefix) && !is_empty_string(uri))
{
FIXME("Prefix autogeneration is not implemented.\n");
return E_NOTIMPL;
}
if (!is_empty_string(uri))
ns = writer_push_ns(This, prefix, prefix_len, uri);
}
if (ns)
write_output_attribute(This, ns->prefix, ns->prefix_len, local, local_len, value);
else
write_output_attribute(This, prefix, prefix_len, local, local_len, value);
return S_OK;
}
@ -923,63 +1071,6 @@ static HRESULT WINAPI xmlwriter_WriteDocType(IXmlWriter *iface, LPCWSTR pwszName
return E_NOTIMPL;
}
static HRESULT is_valid_ncname(const WCHAR *str, int *out)
{
int len = 0;
*out = 0;
if (!str || !*str)
return S_OK;
while (*str)
{
if (!is_ncnamechar(*str))
return WC_E_NAMECHARACTER;
len++;
str++;
}
*out = len;
return S_OK;
}
static BOOL is_empty_string(const WCHAR *str)
{
return !str || !*str;
}
static struct ns *writer_find_ns(xmlwriter *writer, const WCHAR *prefix, const WCHAR *uri)
{
struct element *element;
struct ns *ns;
if (is_empty_string(prefix) && is_empty_string(uri))
return NULL;
LIST_FOR_EACH_ENTRY(element, &writer->elements, struct element, entry)
{
LIST_FOR_EACH_ENTRY(ns, &element->ns, struct ns, entry)
{
if (!uri)
{
if (!ns->prefix) continue;
if (!strcmpW(ns->prefix, prefix))
return ns;
}
else if (!strcmpW(uri, ns->uri))
{
if (prefix && !*prefix)
return NULL;
if (!prefix || !strcmpW(prefix, ns->prefix))
return ns;
}
}
}
return NULL;
}
static HRESULT WINAPI xmlwriter_WriteElementString(IXmlWriter *iface, LPCWSTR prefix,
LPCWSTR local_name, LPCWSTR uri, LPCWSTR value)
{