[PATCH 2/2] xmllite/writer: Improve namespace support in WriteAttributeString().
Nikolay Sivov
nsivov at codeweavers.com
Wed Sep 12 08:25:52 CDT 2018
Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
dlls/xmllite/tests/writer.c | 67 ++++++++--
dlls/xmllite/writer.c | 251 ++++++++++++++++++++++++------------
2 files changed, 224 insertions(+), 94 deletions(-)
diff --git a/dlls/xmllite/tests/writer.c b/dlls/xmllite/tests/writer.c
index 972eb96d9b..1710462deb 100644
--- a/dlls/xmllite/tests/writer.c
+++ b/dlls/xmllite/tests/writer.c
@@ -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)
diff --git a/dlls/xmllite/writer.c b/dlls/xmllite/writer.c
index 31c11d9340..aa6ff7f92a 100644
--- a/dlls/xmllite/writer.c
+++ b/dlls/xmllite/writer.c
@@ -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,7 +445,8 @@ 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));
- write_output_buffer(output, data, len);
+ 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)
{
+ 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);
+}
+
+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(ns_prefix), debugstr_w(local_name),
- debugstr_w(ns_uri), debugstr_w(value));
+ 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)
{
--
2.18.0
More information about the wine-devel
mailing list