webservices: Support appending text with multiple WsWriteText calls.

Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Hans Leidekker 2017-04-28 12:12:46 +02:00 committed by Alexandre Julliard
parent 655e5e8c70
commit 40b5dc9541
2 changed files with 276 additions and 29 deletions

View file

@ -2301,9 +2301,13 @@ static void test_field_options(void)
static void test_WsWriteText(void)
{
static const WCHAR testW[] = {'t','e','s','t'};
WS_XML_STRING localname = {1, (BYTE *)"t"}, localname2 = {1, (BYTE *)"a"}, ns = {0, NULL};
HRESULT hr;
WS_XML_WRITER *writer;
WS_XML_UTF8_TEXT utf8 = {{WS_XML_TEXT_TYPE_UTF8}};
WS_XML_UTF16_TEXT utf16 = {{WS_XML_TEXT_TYPE_UTF16}};
WS_XML_GUID_TEXT guid = {{WS_XML_TEXT_TYPE_GUID}};
hr = WsCreateWriter( NULL, 0, &writer, NULL );
ok( hr == S_OK, "got %08x\n", hr );
@ -2316,6 +2320,177 @@ static void test_WsWriteText(void)
hr = WsWriteText( writer, &utf8.text, NULL );
ok( hr == WS_E_INVALID_FORMAT, "got %08x\n", hr );
hr = set_output( writer );
ok( hr == S_OK, "got %08x\n", hr );
/* element, utf8 */
hr = WsWriteStartElement( writer, NULL, &localname, &ns, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteText( writer, &utf8.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "<t>test", __LINE__ );
utf8.value.bytes = (BYTE *)"tset";
hr = WsWriteText( writer, &utf8.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "<t>testtset", __LINE__ );
hr = WsWriteEndElement( writer, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "<t>testtset</t>", __LINE__ );
hr = set_output( writer );
ok( hr == S_OK, "got %08x\n", hr );
/* attribute, utf8 */
hr = WsWriteStartElement( writer, NULL, &localname, &ns, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteStartAttribute( writer, NULL, &localname2, &ns, FALSE, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteText( writer, &utf8.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "", __LINE__ );
utf8.value.bytes = (BYTE *)"test";
hr = WsWriteText( writer, &utf8.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "", __LINE__ );
hr = WsWriteEndAttribute( writer, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteEndElement( writer, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "<t a=\"tsettest\"/>", __LINE__ );
hr = set_output( writer );
ok( hr == S_OK, "got %08x\n", hr );
/* element, utf16 */
hr = WsWriteStartElement( writer, NULL, &localname, &ns, NULL );
ok( hr == S_OK, "got %08x\n", hr );
utf16.bytes = (BYTE *)testW;
utf16.byteCount = sizeof(testW);
hr = WsWriteText( writer, &utf16.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "<t>test", __LINE__ );
hr = WsWriteText( writer, &utf16.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "<t>testtest", __LINE__ );
hr = WsWriteEndElement( writer, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "<t>testtest</t>", __LINE__ );
hr = set_output( writer );
ok( hr == S_OK, "got %08x\n", hr );
/* attribute, utf16 */
hr = WsWriteStartElement( writer, NULL, &localname, &ns, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteStartAttribute( writer, NULL, &localname2, &ns, FALSE, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteText( writer, &utf16.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "", __LINE__ );
hr = WsWriteText( writer, &utf16.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "", __LINE__ );
hr = WsWriteEndAttribute( writer, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteEndElement( writer, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "<t a=\"testtest\"/>", __LINE__ );
hr = set_output( writer );
ok( hr == S_OK, "got %08x\n", hr );
/* element, guid */
hr = WsWriteStartElement( writer, NULL, &localname, &ns, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteText( writer, &guid.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "<t>00000000-0000-0000-0000-000000000000", __LINE__ );
hr = WsWriteText( writer, &guid.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "<t>00000000-0000-0000-0000-00000000000000000000-0000-0000-0000-000000000000",
__LINE__ );
/* continue with different text type */
hr = WsWriteText( writer, &utf8.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "<t>00000000-0000-0000-0000-00000000000000000000-0000-0000-0000-000000000000test",
__LINE__ );
hr = WsWriteEndElement( writer, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = set_output( writer );
ok( hr == S_OK, "got %08x\n", hr );
/* attribute, guid */
hr = WsWriteStartElement( writer, NULL, &localname, &ns, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteStartAttribute( writer, NULL, &localname2, &ns, FALSE, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteText( writer, &guid.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "", __LINE__ );
hr = WsWriteText( writer, &guid.text, NULL );
ok( hr == WS_E_INVALID_OPERATION, "got %08x\n", hr );
hr = set_output( writer );
ok( hr == S_OK, "got %08x\n", hr );
/* attribute, mix allowed text types */
hr = WsWriteStartElement( writer, NULL, &localname, &ns, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteStartAttribute( writer, NULL, &localname2, &ns, FALSE, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteText( writer, &utf8.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteText( writer, &utf16.text, NULL );
todo_wine ok( hr == WS_E_INVALID_OPERATION, "got %08x\n", hr );
hr = set_output( writer );
ok( hr == S_OK, "got %08x\n", hr );
/* cdata */
hr = WsWriteStartElement( writer, NULL, &localname, &ns, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteStartCData( writer, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteText( writer, &utf8.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteText( writer, &guid.text, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteEndCData( writer, NULL );
ok( hr == S_OK, "got %08x\n", hr );
hr = WsWriteEndElement( writer, NULL );
ok( hr == S_OK, "got %08x\n", hr );
check_output( writer, "<t><![CDATA[test00000000-0000-0000-0000-000000000000]]></t>", __LINE__ );
WsFreeWriter( writer );
}

View file

@ -1447,14 +1447,19 @@ static ULONG encode_base64( const unsigned char *bin, ULONG len, unsigned char *
return i;
}
static HRESULT text_to_utf8text( const WS_XML_TEXT *text, WS_XML_UTF8_TEXT **ret )
static HRESULT text_to_utf8text( const WS_XML_TEXT *text, const WS_XML_UTF8_TEXT *old, WS_XML_UTF8_TEXT **ret )
{
ULONG len_old = old ? old->value.length : 0;
switch (text->textType)
{
case WS_XML_TEXT_TYPE_UTF8:
{
const WS_XML_UTF8_TEXT *src = (const WS_XML_UTF8_TEXT *)text;
if (!(*ret = alloc_utf8_text( src->value.bytes, src->value.length ))) return E_OUTOFMEMORY;
if (!(*ret = alloc_utf8_text( NULL, len_old + src->value.length ))) return E_OUTOFMEMORY;
if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
memcpy( (*ret)->value.bytes + len_old, src->value.bytes, src->value.length );
return S_OK;
}
case WS_XML_TEXT_TYPE_UTF16:
@ -1465,23 +1470,28 @@ static HRESULT text_to_utf8text( const WS_XML_TEXT *text, WS_XML_UTF8_TEXT **ret
if (src->byteCount % sizeof(WCHAR)) return E_INVALIDARG;
len_utf8 = WideCharToMultiByte( CP_UTF8, 0, str, len, NULL, 0, NULL, NULL );
if (!(*ret = alloc_utf8_text( NULL, len_utf8 ))) return E_OUTOFMEMORY;
WideCharToMultiByte( CP_UTF8, 0, str, len, (char *)(*ret)->value.bytes, (*ret)->value.length, NULL, NULL );
if (!(*ret = alloc_utf8_text( NULL, len_old + len_utf8 ))) return E_OUTOFMEMORY;
if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
WideCharToMultiByte( CP_UTF8, 0, str, len, (char *)(*ret)->value.bytes + len_old, len_utf8, NULL, NULL );
return S_OK;
}
case WS_XML_TEXT_TYPE_BASE64:
{
const WS_XML_BASE64_TEXT *base64 = (const WS_XML_BASE64_TEXT *)text;
ULONG len = ((4 * base64->length / 3) + 3) & ~3;
if (!(*ret = alloc_utf8_text( NULL, len ))) return E_OUTOFMEMORY;
(*ret)->value.length = encode_base64( base64->bytes, base64->length, (*ret)->value.bytes );
if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
(*ret)->value.length = encode_base64( base64->bytes, base64->length, (*ret)->value.bytes + len_old ) + len_old;
return S_OK;
}
case WS_XML_TEXT_TYPE_BOOL:
{
const WS_XML_BOOL_TEXT *bool_text = (const WS_XML_BOOL_TEXT *)text;
if (!(*ret = alloc_utf8_text( NULL, 5 ))) return E_OUTOFMEMORY;
(*ret)->value.length = format_bool( &bool_text->value, (*ret)->value.bytes );
if (!(*ret = alloc_utf8_text( NULL, len_old + 5 ))) return E_OUTOFMEMORY;
if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
(*ret)->value.length = format_bool( &bool_text->value, (*ret)->value.bytes + len_old ) + len_old;
return S_OK;
}
case WS_XML_TEXT_TYPE_INT32:
@ -1489,7 +1499,10 @@ static HRESULT text_to_utf8text( const WS_XML_TEXT *text, WS_XML_UTF8_TEXT **ret
const WS_XML_INT32_TEXT *int32_text = (const WS_XML_INT32_TEXT *)text;
unsigned char buf[12]; /* "-2147483648" */
ULONG len = format_int32( &int32_text->value, buf );
if (!(*ret = alloc_utf8_text( buf, len ))) return E_OUTOFMEMORY;
if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
memcpy( (*ret)->value.bytes + len_old, buf, len );
return S_OK;
}
case WS_XML_TEXT_TYPE_INT64:
@ -1497,7 +1510,10 @@ static HRESULT text_to_utf8text( const WS_XML_TEXT *text, WS_XML_UTF8_TEXT **ret
const WS_XML_INT64_TEXT *int64_text = (const WS_XML_INT64_TEXT *)text;
unsigned char buf[21]; /* "-9223372036854775808" */
ULONG len = format_int64( &int64_text->value, buf );
if (!(*ret = alloc_utf8_text( buf, len ))) return E_OUTOFMEMORY;
if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
memcpy( (*ret)->value.bytes + len_old, buf, len );
return S_OK;
}
case WS_XML_TEXT_TYPE_UINT64:
@ -1505,7 +1521,10 @@ static HRESULT text_to_utf8text( const WS_XML_TEXT *text, WS_XML_UTF8_TEXT **ret
const WS_XML_UINT64_TEXT *uint64_text = (const WS_XML_UINT64_TEXT *)text;
unsigned char buf[21]; /* "18446744073709551615" */
ULONG len = format_uint64( &uint64_text->value, buf );
if (!(*ret = alloc_utf8_text( buf, len ))) return E_OUTOFMEMORY;
if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
memcpy( (*ret)->value.bytes + len_old, buf, len );
return S_OK;
}
case WS_XML_TEXT_TYPE_DOUBLE:
@ -1519,28 +1538,37 @@ static HRESULT text_to_utf8text( const WS_XML_TEXT *text, WS_XML_UTF8_TEXT **ret
len = format_double( &double_text->value, buf );
restore_fpword( fpword );
if (!len) return E_NOTIMPL;
if (!(*ret = alloc_utf8_text( buf, len ))) return E_OUTOFMEMORY;
if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY;
if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
memcpy( (*ret)->value.bytes + len_old, buf, len );
return S_OK;
}
case WS_XML_TEXT_TYPE_GUID:
{
const WS_XML_GUID_TEXT *id = (const WS_XML_GUID_TEXT *)text;
if (!(*ret = alloc_utf8_text( NULL, 37 ))) return E_OUTOFMEMORY;
(*ret)->value.length = format_guid( &id->value, (*ret)->value.bytes );
if (!(*ret = alloc_utf8_text( NULL, len_old + 37 ))) return E_OUTOFMEMORY;
if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
(*ret)->value.length = format_guid( &id->value, (*ret)->value.bytes + len_old ) + len_old;
return S_OK;
}
case WS_XML_TEXT_TYPE_UNIQUE_ID:
{
const WS_XML_UNIQUE_ID_TEXT *id = (const WS_XML_UNIQUE_ID_TEXT *)text;
if (!(*ret = alloc_utf8_text( NULL, 46 ))) return E_OUTOFMEMORY;
(*ret)->value.length = format_urn( &id->value, (*ret)->value.bytes );
if (!(*ret = alloc_utf8_text( NULL, len_old + 46 ))) return E_OUTOFMEMORY;
if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
(*ret)->value.length = format_urn( &id->value, (*ret)->value.bytes + len_old ) + len_old;
return S_OK;
}
case WS_XML_TEXT_TYPE_DATETIME:
{
const WS_XML_DATETIME_TEXT *dt = (const WS_XML_DATETIME_TEXT *)text;
if (!(*ret = alloc_utf8_text( NULL, 34 ))) return E_OUTOFMEMORY;
(*ret)->value.length = format_datetime( &dt->value, (*ret)->value.bytes );
if (!(*ret = alloc_utf8_text( NULL, len_old + 34 ))) return E_OUTOFMEMORY;
if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old );
(*ret)->value.length = format_datetime( &dt->value, (*ret)->value.bytes + len_old ) + len_old;
return S_OK;
}
default:
@ -1552,11 +1580,37 @@ static HRESULT text_to_utf8text( const WS_XML_TEXT *text, WS_XML_UTF8_TEXT **ret
static HRESULT write_set_attribute_value( struct writer *writer, const WS_XML_TEXT *value )
{
WS_XML_ELEMENT_NODE *elem = &writer->current->hdr;
WS_XML_UTF8_TEXT *utf8;
WS_XML_UTF8_TEXT *new, *old = (WS_XML_UTF8_TEXT *)elem->attributes[elem->attributeCount - 1]->value;
HRESULT hr;
if ((hr = text_to_utf8text( value, &utf8 )) != S_OK) return hr;
elem->attributes[elem->attributeCount - 1]->value = &utf8->text;
switch (value->textType)
{
case WS_XML_TEXT_TYPE_UTF8:
case WS_XML_TEXT_TYPE_UTF16:
case WS_XML_TEXT_TYPE_BASE64:
break;
case WS_XML_TEXT_TYPE_BOOL:
case WS_XML_TEXT_TYPE_INT32:
case WS_XML_TEXT_TYPE_INT64:
case WS_XML_TEXT_TYPE_UINT64:
case WS_XML_TEXT_TYPE_DOUBLE:
case WS_XML_TEXT_TYPE_GUID:
case WS_XML_TEXT_TYPE_UNIQUE_ID:
case WS_XML_TEXT_TYPE_DATETIME:
if (old) return WS_E_INVALID_OPERATION;
break;
default:
FIXME( "unhandled text type %u\n", value->textType );
return E_NOTIMPL;
}
if ((hr = text_to_utf8text( value, old, &new )) != S_OK) return hr;
heap_free( old );
elem->attributes[elem->attributeCount - 1]->value = &new->text;
return S_OK;
}
@ -1572,7 +1626,7 @@ static HRESULT write_add_text_node( struct writer *writer, const WS_XML_TEXT *va
node_type( writer->current ) != WS_XML_NODE_TYPE_CDATA) return WS_E_INVALID_FORMAT;
if (!(node = alloc_node( WS_XML_NODE_TYPE_TEXT ))) return E_OUTOFMEMORY;
if ((hr = text_to_utf8text( value, &utf8 )) != S_OK)
if ((hr = text_to_utf8text( value, NULL, &utf8 )) != S_OK)
{
heap_free( node );
return hr;
@ -1584,7 +1638,7 @@ static HRESULT write_add_text_node( struct writer *writer, const WS_XML_TEXT *va
return S_OK;
}
static HRESULT write_text( struct writer *writer )
static HRESULT write_text( struct writer *writer, ULONG offset )
{
const WS_XML_TEXT_NODE *text = (const WS_XML_TEXT_NODE *)writer->current;
const WS_XML_UTF8_TEXT *utf8 = (const WS_XML_UTF8_TEXT *)text->text;
@ -1594,12 +1648,12 @@ static HRESULT write_text( struct writer *writer )
if (node_type( writer->current->parent ) == WS_XML_NODE_TYPE_ELEMENT)
{
const struct escape *escapes[3] = { &escape_lt, &escape_gt, &escape_amp };
return write_bytes_escape( writer, utf8->value.bytes, utf8->value.length, escapes, 3 );
return write_bytes_escape( writer, utf8->value.bytes + offset, utf8->value.length - offset, escapes, 3 );
}
else if (node_type( writer->current->parent ) == WS_XML_NODE_TYPE_CDATA)
{
if ((hr = write_grow_buffer( writer, utf8->value.length )) != S_OK) return hr;
write_bytes( writer, utf8->value.bytes, utf8->value.length );
if ((hr = write_grow_buffer( writer, utf8->value.length - offset )) != S_OK) return hr;
write_bytes( writer, utf8->value.bytes + offset, utf8->value.length - offset );
return S_OK;
}
@ -1608,10 +1662,28 @@ static HRESULT write_text( struct writer *writer )
static HRESULT write_text_node( struct writer *writer, const WS_XML_TEXT *text )
{
ULONG offset;
HRESULT hr;
if ((hr = write_flush( writer )) != S_OK) return hr;
if ((hr = write_add_text_node( writer, text )) != S_OK) return hr;
if ((hr = write_text( writer )) != S_OK) return hr;
if (node_type( writer->current ) != WS_XML_NODE_TYPE_TEXT)
{
offset = 0;
if ((hr = write_add_text_node( writer, text )) != S_OK) return hr;
}
else
{
WS_XML_TEXT_NODE *node = (WS_XML_TEXT_NODE *)writer->current;
WS_XML_UTF8_TEXT *new, *old = (WS_XML_UTF8_TEXT *)node->text;
offset = old->value.length;
if ((hr = text_to_utf8text( text, old, &new )) != S_OK) return hr;
heap_free( old );
node->text = &new->text;
}
if ((hr = write_text( writer, offset )) != S_OK) return hr;
writer->state = WRITER_STATE_TEXT;
return S_OK;
}
@ -3044,7 +3116,7 @@ static HRESULT write_tree_node( struct writer *writer )
case WS_XML_NODE_TYPE_TEXT:
if (writer->state == WRITER_STATE_STARTELEMENT && (hr = write_endstartelement( writer )) != S_OK)
return hr;
if ((hr = write_text( writer )) != S_OK) return hr;
if ((hr = write_text( writer, 0 )) != S_OK) return hr;
writer->state = WRITER_STATE_TEXT;
return S_OK;