diff --git a/dlls/webservices/reader.c b/dlls/webservices/reader.c index 9d75f94b8ef..254efb9f0e0 100644 --- a/dlls/webservices/reader.c +++ b/dlls/webservices/reader.c @@ -5073,7 +5073,7 @@ static HRESULT read_type_next_element_node( struct reader *reader, const WS_XML_ return WS_E_INVALID_FORMAT; } -ULONG get_type_size( WS_TYPE type, const WS_STRUCT_DESCRIPTION *desc ) +ULONG get_type_size( WS_TYPE type, const void *desc ) { switch (type) { @@ -5119,12 +5119,19 @@ ULONG get_type_size( WS_TYPE type, const WS_STRUCT_DESCRIPTION *desc ) case WS_XML_QNAME_TYPE: return sizeof(WS_XML_QNAME); - case WS_STRUCT_TYPE: - return desc->size; - case WS_DESCRIPTION_TYPE: return sizeof(WS_STRUCT_DESCRIPTION *); + case WS_STRUCT_TYPE: + { + const WS_STRUCT_DESCRIPTION *desc_struct = desc; + return desc_struct->size; + } + case WS_UNION_TYPE: + { + const WS_UNION_DESCRIPTION *desc_union = desc; + return desc_union->size; + } default: ERR( "unhandled type %u\n", type ); return 0; diff --git a/dlls/webservices/tests/writer.c b/dlls/webservices/tests/writer.c index 0a8d753988a..f08c4cee5b4 100644 --- a/dlls/webservices/tests/writer.c +++ b/dlls/webservices/tests/writer.c @@ -3909,6 +3909,122 @@ static void test_dictionary(void) WsFreeWriter( writer ); } +static void test_union_type(void) +{ + static const WCHAR testW[] = {'t','e','s','t',0}; + static WS_XML_STRING str_ns = {0, NULL}, str_a = {1, (BYTE *)"a"}, str_b = {1, (BYTE *)"b"}; + static WS_XML_STRING str_s = {1, (BYTE *)"s"}, str_t = {1, (BYTE *)"t"}; + HRESULT hr; + WS_XML_WRITER *writer; + WS_UNION_DESCRIPTION u; + WS_UNION_FIELD_DESCRIPTION f, f2, *fields[2]; + WS_FIELD_DESCRIPTION f_struct, *fields_struct[1]; + WS_STRUCT_DESCRIPTION s; + enum choice {CHOICE_A, CHOICE_B, CHOICE_NONE}; + struct test + { + enum choice choice; + union + { + const WCHAR *a; + UINT32 b; + } value; + } test; + + hr = WsCreateWriter( NULL, 0, &writer, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + memset( &f, 0, sizeof(f) ); + f.value = CHOICE_A; + f.field.mapping = WS_ELEMENT_FIELD_MAPPING; + f.field.localName = &str_a; + f.field.ns = &str_ns; + f.field.type = WS_WSZ_TYPE; + f.field.offset = FIELD_OFFSET(struct test, value.a); + fields[0] = &f; + + memset( &f2, 0, sizeof(f2) ); + f2.value = CHOICE_B; + f2.field.mapping = WS_ELEMENT_FIELD_MAPPING; + f2.field.localName = &str_b; + f2.field.ns = &str_ns; + f2.field.type = WS_UINT32_TYPE; + f2.field.offset = FIELD_OFFSET(struct test, value.b); + fields[1] = &f2; + + memset( &u, 0, sizeof(u) ); + u.size = sizeof(struct test); + u.alignment = TYPE_ALIGNMENT(struct test); + u.fields = fields; + u.fieldCount = 2; + u.enumOffset = FIELD_OFFSET(struct test, choice); + u.noneEnumValue = CHOICE_NONE; + + memset( &f_struct, 0, sizeof(f_struct) ); + f_struct.mapping = WS_ELEMENT_CHOICE_FIELD_MAPPING; + f_struct.type = WS_UNION_TYPE; + f_struct.typeDescription = &u; + fields_struct[0] = &f_struct; + + memset( &s, 0, sizeof(s) ); + s.size = sizeof(struct test); + s.alignment = TYPE_ALIGNMENT(struct test); + s.fields = fields_struct; + s.fieldCount = 1; + s.typeLocalName = &str_s; + s.typeNs = &str_ns; + + hr = set_output( writer ); + ok( hr == S_OK, "got %08x\n", hr ); + hr = WsWriteStartElement( writer, NULL, &str_t, &str_ns, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + test.choice = CHOICE_A; + test.value.a = testW; + hr = WsWriteType( writer, WS_ELEMENT_CONTENT_TYPE_MAPPING, WS_STRUCT_TYPE, &s, + WS_WRITE_REQUIRED_VALUE, &test, sizeof(test), NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + hr = WsWriteEndElement( writer, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + check_output( writer, "test", __LINE__ ); + + hr = set_output( writer ); + ok( hr == S_OK, "got %08x\n", hr ); + hr = WsWriteStartElement( writer, NULL, &str_t, &str_ns, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + test.choice = CHOICE_B; + test.value.b = 123; + hr = WsWriteType( writer, WS_ELEMENT_CONTENT_TYPE_MAPPING, WS_STRUCT_TYPE, &s, + WS_WRITE_REQUIRED_VALUE, &test, sizeof(test), NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + hr = WsWriteEndElement( writer, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + check_output( writer, "123", __LINE__ ); + + hr = set_output( writer ); + ok( hr == S_OK, "got %08x\n", hr ); + hr = WsWriteStartElement( writer, NULL, &str_t, &str_ns, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + test.choice = CHOICE_NONE; + hr = WsWriteType( writer, WS_ELEMENT_CONTENT_TYPE_MAPPING, WS_STRUCT_TYPE, &s, + WS_WRITE_REQUIRED_VALUE, &test, sizeof(test), NULL ); + ok( hr == WS_E_INVALID_FORMAT, "got %08x\n", hr ); + + hr = set_output( writer ); + ok( hr == S_OK, "got %08x\n", hr ); + hr = WsWriteStartElement( writer, NULL, &str_t, &str_ns, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + test.choice = CHOICE_NONE; + f_struct.options = WS_FIELD_OPTIONAL; + hr = WsWriteType( writer, WS_ELEMENT_CONTENT_TYPE_MAPPING, WS_STRUCT_TYPE, &s, + WS_WRITE_REQUIRED_VALUE, &test, sizeof(test), NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + hr = WsWriteEndElement( writer, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + check_output( writer, "", __LINE__ ); + + WsFreeWriter( writer ); +} + START_TEST(writer) { test_WsCreateWriter(); @@ -3949,4 +4065,5 @@ START_TEST(writer) test_binary_encoding(); test_namespaces(); test_dictionary(); + test_union_type(); } diff --git a/dlls/webservices/webservices_private.h b/dlls/webservices/webservices_private.h index 19385871642..68837db642b 100644 --- a/dlls/webservices/webservices_private.h +++ b/dlls/webservices/webservices_private.h @@ -67,7 +67,7 @@ WS_TYPE map_value_type( WS_VALUE_TYPE ) DECLSPEC_HIDDEN; BOOL set_fpword( unsigned short, unsigned short * ) DECLSPEC_HIDDEN; void restore_fpword( unsigned short ) DECLSPEC_HIDDEN; HRESULT set_output( WS_XML_WRITER * ) DECLSPEC_HIDDEN; -ULONG get_type_size( WS_TYPE, const WS_STRUCT_DESCRIPTION * ) DECLSPEC_HIDDEN; +ULONG get_type_size( WS_TYPE, const void * ) DECLSPEC_HIDDEN; HRESULT read_header( WS_XML_READER *, const WS_XML_STRING *, const WS_XML_STRING *, WS_TYPE, const void *, WS_READ_OPTION, WS_HEAP *, void *, ULONG ) DECLSPEC_HIDDEN; diff --git a/dlls/webservices/writer.c b/dlls/webservices/writer.c index 95ebd7c267f..b4793f91a5b 100644 --- a/dlls/webservices/writer.c +++ b/dlls/webservices/writer.c @@ -2923,8 +2923,49 @@ static HRESULT write_type_repeating_element( struct writer *writer, const WS_FIE return hr; } -static HRESULT write_type_struct_field( struct writer *writer, const WS_FIELD_DESCRIPTION *desc, - const char *buf, ULONG offset ) +static HRESULT write_type_field( struct writer *, const WS_FIELD_DESCRIPTION *, const char *, ULONG ); + +static HRESULT write_type_union( struct writer *writer, const WS_UNION_DESCRIPTION *desc, WS_WRITE_OPTION option, + const void *value, ULONG size ) +{ + ULONG i, offset; + const void *ptr; + int enum_value; + HRESULT hr; + + if ((hr = get_value_ptr( option, value, size, desc->size, &ptr )) != S_OK) return hr; + + if (size < sizeof(enum_value)) return E_INVALIDARG; + if ((enum_value = *(int *)(char *)ptr + desc->enumOffset) == desc->noneEnumValue) + { + switch (option) + { + case WS_WRITE_REQUIRED_VALUE: + return WS_E_INVALID_FORMAT; + + case WS_WRITE_NILLABLE_VALUE: + return S_OK; + + default: + ERR( "unhandled write option %u\n", option ); + return E_INVALIDARG; + } + } + + for (i = 0; i < desc->fieldCount; i++) + { + if (desc->fields[i]->value == enum_value) + { + offset = desc->fields[i]->field.offset; + return write_type_field( writer, &desc->fields[i]->field, ptr, offset ); + } + } + + return E_INVALIDARG; +} + +static HRESULT write_type_field( struct writer *writer, const WS_FIELD_DESCRIPTION *desc, const char *buf, + ULONG offset ) { HRESULT hr; WS_TYPE_MAPPING mapping; @@ -2982,6 +3023,11 @@ static HRESULT write_type_struct_field( struct writer *writer, const WS_FIELD_DE mapping = WS_ELEMENT_TYPE_MAPPING; break; + case WS_ELEMENT_CHOICE_FIELD_MAPPING: + if (desc->type != WS_UNION_TYPE || !desc->typeDescription) return E_INVALIDARG; + option = (field_options & WS_FIELD_OPTIONAL) ? WS_WRITE_NILLABLE_VALUE : WS_WRITE_REQUIRED_VALUE; + return write_type_union( writer, desc->typeDescription, option, ptr, size ); + case WS_REPEATING_ELEMENT_FIELD_MAPPING: count = *(const ULONG *)(buf + desc->countOffset); return write_type_repeating_element( writer, desc, *(const char **)ptr, count ); @@ -3043,14 +3089,12 @@ static HRESULT write_type_struct( struct writer *writer, WS_TYPE_MAPPING mapping for (i = 0; i < desc->fieldCount; i++) { offset = desc->fields[i]->offset; - if ((hr = write_type_struct_field( writer, desc->fields[i], ptr, offset )) != S_OK) - return hr; + if ((hr = write_type_field( writer, desc->fields[i], ptr, offset )) != S_OK) return hr; } return S_OK; } - static HRESULT write_type( struct writer *writer, WS_TYPE_MAPPING mapping, WS_TYPE type, const void *desc, WS_WRITE_OPTION option, const void *value, ULONG size ) @@ -4023,7 +4067,7 @@ done: static HRESULT write_param( struct writer *writer, const WS_FIELD_DESCRIPTION *desc, const void *value ) { - return write_type_struct_field( writer, desc, value, 0 ); + return write_type_field( writer, desc, value, 0 ); } static ULONG get_array_len( const WS_PARAMETER_DESCRIPTION *params, ULONG count, ULONG index, const void **args ) diff --git a/include/webservices.h b/include/webservices.h index bea4adee46e..2af333a588d 100644 --- a/include/webservices.h +++ b/include/webservices.h @@ -531,6 +531,21 @@ typedef struct _WS_STRUCT_DESCRIPTION { ULONG structOptions; } WS_STRUCT_DESCRIPTION; +typedef struct _WS_UNION_FIELD_DESCRIPTION { + int value; + WS_FIELD_DESCRIPTION field; +} WS_UNION_FIELD_DESCRIPTION; + +typedef struct _WS_UNION_DESCRIPTION { + ULONG size; + ULONG alignment; + WS_UNION_FIELD_DESCRIPTION **fields; + ULONG fieldCount; + ULONG enumOffset; + int noneEnumValue; + ULONG *valueIndices; +} WS_UNION_DESCRIPTION; + typedef struct _WS_ATTRIBUTE_DESCRIPTION { WS_XML_STRING *attributeLocalName; WS_XML_STRING *attributeNs;