webservices: Add support for reading date values.
Signed-off-by: Hans Leidekker <hans@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
7ff383e5e2
commit
3268ceb573
|
@ -1881,6 +1881,148 @@ static HRESULT str_to_uint64( const unsigned char *str, ULONG len, UINT64 max, U
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
#define TICKS_PER_SEC 10000000
|
||||
#define TICKS_PER_MIN (60 * (ULONGLONG)TICKS_PER_SEC)
|
||||
#define TICKS_PER_HOUR (3600 * (ULONGLONG)TICKS_PER_SEC)
|
||||
#define TICKS_PER_DAY (86400 * (ULONGLONG)TICKS_PER_SEC)
|
||||
#define TICKS_MAX 3155378975999999999
|
||||
|
||||
static const int month_offsets[2][12] =
|
||||
{
|
||||
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
|
||||
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
|
||||
};
|
||||
|
||||
static const int month_days[2][12] =
|
||||
{
|
||||
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
|
||||
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
|
||||
};
|
||||
|
||||
static inline int is_leap_year( int year )
|
||||
{
|
||||
return !(year % 4) && (year % 100 || !(year % 400));
|
||||
}
|
||||
|
||||
static inline int valid_day( int year, int month, int day )
|
||||
{
|
||||
return day > 0 && day <= month_days[is_leap_year( year )][month - 1];
|
||||
}
|
||||
|
||||
static inline int leap_days_before( int year )
|
||||
{
|
||||
return (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;
|
||||
}
|
||||
|
||||
static HRESULT str_to_datetime( const unsigned char *bytes, ULONG len, WS_DATETIME *ret )
|
||||
{
|
||||
const unsigned char *p = bytes, *q;
|
||||
int year, month, day, hour, min, sec, sec_frac = 0, tz_hour, tz_min, tz_neg;
|
||||
|
||||
while (len && read_isspace( *p )) { p++; len--; }
|
||||
while (len && read_isspace( p[len - 1] )) { len--; }
|
||||
|
||||
q = p;
|
||||
while (len && isdigit( *q )) { q++; len--; };
|
||||
if (q - p != 4 || !len || *q != '-') return WS_E_INVALID_FORMAT;
|
||||
year = (p[0] - '0') * 1000 + (p[1] - '0') * 100 + (p[2] - '0') * 10 + p[3] - '0';
|
||||
if (year < 1) return WS_E_INVALID_FORMAT;
|
||||
|
||||
p = ++q; len--;
|
||||
while (len && isdigit( *q )) { q++; len--; };
|
||||
if (q - p != 2 || !len || *q != '-') return WS_E_INVALID_FORMAT;
|
||||
month = (p[0] - '0') * 10 + p[1] - '0';
|
||||
if (month < 1 || month > 12) return WS_E_INVALID_FORMAT;
|
||||
|
||||
p = ++q; len--;
|
||||
while (len && isdigit( *q )) { q++; len--; };
|
||||
if (q - p != 2 || !len || *q != 'T') return WS_E_INVALID_FORMAT;
|
||||
day = (p[0] - '0') * 10 + p[1] - '0';
|
||||
if (!valid_day( year, month, day )) return WS_E_INVALID_FORMAT;
|
||||
|
||||
p = ++q; len--;
|
||||
while (len && isdigit( *q )) { q++; len--; };
|
||||
if (q - p != 2 || !len || *q != ':') return WS_E_INVALID_FORMAT;
|
||||
hour = (p[0] - '0') * 10 + p[1] - '0';
|
||||
if (hour > 24) return WS_E_INVALID_FORMAT;
|
||||
|
||||
p = ++q; len--;
|
||||
while (len && isdigit( *q )) { q++; len--; };
|
||||
if (q - p != 2 || !len || *q != ':') return WS_E_INVALID_FORMAT;
|
||||
min = (p[0] - '0') * 10 + p[1] - '0';
|
||||
if (min > 59 || (min > 0 && hour == 24)) return WS_E_INVALID_FORMAT;
|
||||
|
||||
p = ++q; len--;
|
||||
while (len && isdigit( *q )) { q++; len--; };
|
||||
if (q - p != 2 || !len) return WS_E_INVALID_FORMAT;
|
||||
sec = (p[0] - '0') * 10 + p[1] - '0';
|
||||
if (sec > 59 || (sec > 0 && hour == 24)) return WS_E_INVALID_FORMAT;
|
||||
|
||||
if (*q == '.')
|
||||
{
|
||||
unsigned int i, nb_digits, mul = TICKS_PER_SEC / 10;
|
||||
p = ++q; len--;
|
||||
while (len && isdigit( *q )) { q++; len--; };
|
||||
nb_digits = q - p;
|
||||
if (nb_digits < 1 || nb_digits > 7) return WS_E_INVALID_FORMAT;
|
||||
for (i = 0; i < nb_digits; i++)
|
||||
{
|
||||
sec_frac += (p[i] - '0') * mul;
|
||||
mul /= 10;
|
||||
}
|
||||
}
|
||||
if (*q == 'Z')
|
||||
{
|
||||
if (--len) return WS_E_INVALID_FORMAT;
|
||||
tz_hour = tz_min = tz_neg = 0;
|
||||
ret->format = WS_DATETIME_FORMAT_UTC;
|
||||
}
|
||||
else if (*q == '+' || *q == '-')
|
||||
{
|
||||
tz_neg = (*q == '-') ? 1 : 0;
|
||||
|
||||
p = ++q; len--;
|
||||
while (len && isdigit( *q )) { q++; len--; };
|
||||
if (q - p != 2 || !len || *q != ':') return WS_E_INVALID_FORMAT;
|
||||
tz_hour = (p[0] - '0') * 10 + p[1] - '0';
|
||||
if (tz_hour > 14) return WS_E_INVALID_FORMAT;
|
||||
|
||||
p = ++q; len--;
|
||||
while (len && isdigit( *q )) { q++; len--; };
|
||||
if (q - p != 2 || len) return WS_E_INVALID_FORMAT;
|
||||
tz_min = (p[0] - '0') * 10 + p[1] - '0';
|
||||
if (tz_min > 59 || (tz_min > 0 && tz_hour == 14)) return WS_E_INVALID_FORMAT;
|
||||
|
||||
ret->format = WS_DATETIME_FORMAT_LOCAL;
|
||||
}
|
||||
else return WS_E_INVALID_FORMAT;
|
||||
|
||||
ret->ticks = ((year - 1) * 365 + leap_days_before( year )) * TICKS_PER_DAY;
|
||||
ret->ticks += month_offsets[is_leap_year( year )][month - 1] * TICKS_PER_DAY;
|
||||
ret->ticks += (day - 1) * TICKS_PER_DAY;
|
||||
ret->ticks += hour * TICKS_PER_HOUR;
|
||||
ret->ticks += min * TICKS_PER_MIN;
|
||||
ret->ticks += sec * TICKS_PER_SEC;
|
||||
ret->ticks += sec_frac;
|
||||
|
||||
if (tz_neg)
|
||||
{
|
||||
if (tz_hour * TICKS_PER_HOUR + tz_min * TICKS_PER_MIN + ret->ticks > TICKS_MAX)
|
||||
return WS_E_INVALID_FORMAT;
|
||||
ret->ticks += tz_hour * TICKS_PER_HOUR;
|
||||
ret->ticks += tz_min * TICKS_PER_MIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tz_hour * TICKS_PER_HOUR + tz_min * TICKS_PER_MIN > ret->ticks)
|
||||
return WS_E_INVALID_FORMAT;
|
||||
ret->ticks -= tz_hour * TICKS_PER_HOUR;
|
||||
ret->ticks -= tz_min * TICKS_PER_MIN;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT read_get_node_text( struct reader *reader, WS_XML_UTF8_TEXT **ret )
|
||||
{
|
||||
WS_XML_TEXT_NODE *text;
|
||||
|
@ -2570,6 +2712,53 @@ static HRESULT read_type_enum( struct reader *reader, WS_TYPE_MAPPING mapping,
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT read_type_datetime( struct reader *reader, WS_TYPE_MAPPING mapping,
|
||||
const WS_XML_STRING *localname, const WS_XML_STRING *ns,
|
||||
const WS_DATETIME_DESCRIPTION *desc, WS_READ_OPTION option,
|
||||
WS_HEAP *heap, void *ret, ULONG size )
|
||||
{
|
||||
WS_XML_UTF8_TEXT *utf8;
|
||||
HRESULT hr;
|
||||
WS_DATETIME val = {0, WS_DATETIME_FORMAT_UTC};
|
||||
BOOL found;
|
||||
|
||||
if (desc) FIXME( "ignoring description\n" );
|
||||
|
||||
if ((hr = read_get_text( reader, mapping, localname, ns, &utf8, &found )) != S_OK) return hr;
|
||||
if (found && (hr = str_to_datetime( utf8->value.bytes, utf8->value.length, &val )) != S_OK) return hr;
|
||||
|
||||
switch (option)
|
||||
{
|
||||
case WS_READ_REQUIRED_VALUE:
|
||||
if (!found) return WS_E_INVALID_FORMAT;
|
||||
if (size != sizeof(WS_DATETIME)) return E_INVALIDARG;
|
||||
*(WS_DATETIME *)ret = val;
|
||||
break;
|
||||
|
||||
case WS_READ_REQUIRED_POINTER:
|
||||
if (!found) return WS_E_INVALID_FORMAT;
|
||||
/* fall through */
|
||||
|
||||
case WS_READ_OPTIONAL_POINTER:
|
||||
{
|
||||
WS_DATETIME *heap_val = NULL;
|
||||
if (size != sizeof(heap_val)) return E_INVALIDARG;
|
||||
if (found)
|
||||
{
|
||||
if (!(heap_val = ws_alloc( heap, sizeof(*heap_val) ))) return WS_E_QUOTA_EXCEEDED;
|
||||
*heap_val = val;
|
||||
}
|
||||
*(WS_DATETIME **)ret = heap_val;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
FIXME( "read option %u not supported\n", option );
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static BOOL is_empty_text_node( const struct node *node )
|
||||
{
|
||||
const WS_XML_TEXT_NODE *text = (const WS_XML_TEXT_NODE *)node;
|
||||
|
@ -2643,6 +2832,9 @@ static ULONG get_type_size( WS_TYPE type, const WS_STRUCT_DESCRIPTION *desc )
|
|||
case WS_UINT64_TYPE:
|
||||
return sizeof(INT64);
|
||||
|
||||
case WS_DATETIME_TYPE:
|
||||
return sizeof(WS_DATETIME);
|
||||
|
||||
case WS_WSZ_TYPE:
|
||||
return sizeof(WCHAR *);
|
||||
|
||||
|
@ -2746,6 +2938,7 @@ static WS_READ_OPTION map_field_options( WS_TYPE type, ULONG options )
|
|||
case WS_UINT32_TYPE:
|
||||
case WS_UINT64_TYPE:
|
||||
case WS_ENUM_TYPE:
|
||||
case WS_DATETIME_TYPE:
|
||||
return WS_READ_REQUIRED_VALUE;
|
||||
|
||||
case WS_WSZ_TYPE:
|
||||
|
@ -2979,6 +3172,11 @@ static HRESULT read_type( struct reader *reader, WS_TYPE_MAPPING mapping, WS_TYP
|
|||
return hr;
|
||||
break;
|
||||
|
||||
case WS_DATETIME_TYPE:
|
||||
if ((hr = read_type_datetime( reader, mapping, localname, ns, desc, option, heap, value, size )) != S_OK)
|
||||
return hr;
|
||||
break;
|
||||
|
||||
default:
|
||||
FIXME( "type %u not supported\n", type );
|
||||
return E_NOTIMPL;
|
||||
|
|
|
@ -2924,6 +2924,91 @@ static void test_WsResetHeap(void)
|
|||
WsFreeHeap( heap );
|
||||
}
|
||||
|
||||
static void test_datetime(void)
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
const char *str;
|
||||
HRESULT hr;
|
||||
__int64 ticks;
|
||||
WS_DATETIME_FORMAT format;
|
||||
}
|
||||
tests[] =
|
||||
{
|
||||
{"<t>0000-01-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0001-01-01T00:00:00Z</t>", S_OK, 0, WS_DATETIME_FORMAT_UTC},
|
||||
{"<t>0001-01-01T00:00:00.Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0001-01-01T00:00:00.0Z</t>", S_OK, 0, WS_DATETIME_FORMAT_UTC},
|
||||
{"<t>0001-01-01T00:00:00.1Z</t>", S_OK, 0x0000f4240, WS_DATETIME_FORMAT_UTC},
|
||||
{"<t>0001-01-01T00:00:00.01Z</t>", S_OK, 0x0000186a0, WS_DATETIME_FORMAT_UTC},
|
||||
{"<t>0001-01-01T00:00:00.0000001Z</t>", S_OK, 1, WS_DATETIME_FORMAT_UTC},
|
||||
{"<t>0001-01-01T00:00:00.9999999Z</t>", S_OK, 0x00098967f, WS_DATETIME_FORMAT_UTC},
|
||||
{"<t>0001-01-01T00:00:00.0000000Z</t>", S_OK, 0, WS_DATETIME_FORMAT_UTC},
|
||||
{"<t>0001-01-01T00:00:00.00000001Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0001-01-01T00:00:00Z-</t>", WS_E_INVALID_FORMAT, 0},
|
||||
{"<t>-0001-01-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0001-00-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0001-13-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0001-12-01T00:00:00Z</t>", S_OK, 0x1067555f88000, WS_DATETIME_FORMAT_UTC},
|
||||
{"<t>0001-01-00T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>2001-01-32T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>2001-01-31T00:00:00Z</t>", S_OK, 0x8c2592fe3794000, WS_DATETIME_FORMAT_UTC},
|
||||
{"<t>1900-02-29T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>2000-02-29T00:00:00Z</t>", S_OK, 0x8c1505f0e438000, 0},
|
||||
{"<t>2001-02-29T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>2001-02-28T00:00:00Z</t>", S_OK, 0x8c26f30870a4000, WS_DATETIME_FORMAT_UTC},
|
||||
{"<t>0001-00-01U00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0001-01-01T24:00:00Z</t>", S_OK, 0xc92a69c000, WS_DATETIME_FORMAT_UTC},
|
||||
{"<t>0001-01-01T24:00:01Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0001-01-01T00:60:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0001-01-01T00:00:60Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0001-01-01T00:00:00Y</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0001-01-01T00:00:00+00:01</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0001-01-01T00:00:00-00:01</t>", S_OK, 0x023c34600, WS_DATETIME_FORMAT_LOCAL},
|
||||
{"<t>9999-12-31T24:00:00+00:01</t>", S_OK, 0x2bca2875d073fa00, WS_DATETIME_FORMAT_LOCAL},
|
||||
{"<t>9999-12-31T24:00:00-00:01</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0002-01-01T00:00:00+14:01</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0002-01-01T00:00:00+15:00</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0002-01-01T00:00:00+13:60</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>0002-01-01T00:00:00+13:59</t>", S_OK, 0x11e5c43cc5600, WS_DATETIME_FORMAT_LOCAL},
|
||||
{"<t>0002-01-01T00:00:00+01:00</t>", S_OK, 0x11ec917025800, WS_DATETIME_FORMAT_LOCAL},
|
||||
{"<t>2016-01-01T00:00:00-01:00</t>", S_OK, 0x8d31246dfbba800, WS_DATETIME_FORMAT_LOCAL},
|
||||
{"<t>2016-01-01T00:00:00Z</t>", S_OK, 0x8d3123e7df74000, WS_DATETIME_FORMAT_UTC},
|
||||
{"<t> 2016-01-02T03:04:05Z </t>", S_OK, 0x8d313215fb64080, WS_DATETIME_FORMAT_UTC},
|
||||
{"<t>+2016-01-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t></t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>01-01-01T00:00:00Z</t>", WS_E_INVALID_FORMAT, 0, 0},
|
||||
{"<t>1601-01-01T00:00:00Z</t>", S_OK, 0x701ce1722770000, WS_DATETIME_FORMAT_UTC},
|
||||
};
|
||||
HRESULT hr;
|
||||
WS_XML_READER *reader;
|
||||
WS_HEAP *heap;
|
||||
WS_DATETIME date;
|
||||
ULONG i;
|
||||
|
||||
hr = WsCreateHeap( 1 << 16, 0, NULL, 0, &heap, NULL );
|
||||
ok( hr == S_OK, "got %08x\n", hr );
|
||||
|
||||
hr = WsCreateReader( NULL, 0, &reader, NULL ) ;
|
||||
ok( hr == S_OK, "got %08x\n", hr );
|
||||
for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
|
||||
{
|
||||
memset( &date, 0, sizeof(date) );
|
||||
prepare_type_test( reader, tests[i].str, strlen(tests[i].str) );
|
||||
hr = WsReadType( reader, WS_ELEMENT_CONTENT_TYPE_MAPPING, WS_DATETIME_TYPE, NULL,
|
||||
WS_READ_REQUIRED_VALUE, heap, &date, sizeof(date), NULL );
|
||||
ok( hr == tests[i].hr, "%u: got %08x\n", i, hr );
|
||||
if (hr == S_OK)
|
||||
{
|
||||
ok( date.ticks == tests[i].ticks, "%u: got %x%08x\n", i, (ULONG)(date.ticks >> 32), (ULONG)date.ticks );
|
||||
ok( date.format == tests[i].format, "%u: got %u\n", i, date.format );
|
||||
}
|
||||
}
|
||||
|
||||
WsFreeReader( reader );
|
||||
WsFreeHeap( heap );
|
||||
}
|
||||
|
||||
START_TEST(reader)
|
||||
{
|
||||
test_WsCreateError();
|
||||
|
@ -2949,4 +3034,5 @@ START_TEST(reader)
|
|||
test_complex_struct_type();
|
||||
test_repeating_element();
|
||||
test_WsResetHeap();
|
||||
test_datetime();
|
||||
}
|
||||
|
|
|
@ -57,6 +57,8 @@ typedef struct _WS_PARAMETER_DESCRIPTION WS_PARAMETER_DESCRIPTION;
|
|||
typedef struct _WS_OPERATION_CONTEXT WS_OPERATION_CONTEXT;
|
||||
typedef struct _WS_CALL_PROPERTY WS_CALL_PROPERTY;
|
||||
typedef struct _WS_DOUBLE_DESCRIPTION WS_DOUBLE_DESCRIPTION;
|
||||
typedef struct _WS_DATETIME WS_DATETIME;
|
||||
typedef struct _WS_DATETIME_DESCRIPTION WS_DATETIME_DESCRIPTION;
|
||||
|
||||
struct _WS_STRUCT_DESCRIPTION;
|
||||
struct _WS_XML_STRING;
|
||||
|
@ -989,6 +991,22 @@ struct _WS_CALL_PROPERTY {
|
|||
ULONG valueSize;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
WS_DATETIME_FORMAT_UTC,
|
||||
WS_DATETIME_FORMAT_LOCAL,
|
||||
WS_DATETIME_FORMAT_NONE
|
||||
} WS_DATETIME_FORMAT;
|
||||
|
||||
struct _WS_DATETIME {
|
||||
unsigned __int64 ticks;
|
||||
WS_DATETIME_FORMAT format;
|
||||
};
|
||||
|
||||
struct _WS_DATETIME_DESCRIPTION {
|
||||
WS_DATETIME minValue;
|
||||
WS_DATETIME maxValue;
|
||||
};
|
||||
|
||||
HRESULT WINAPI WsAlloc(WS_HEAP*, SIZE_T, void**, WS_ERROR*);
|
||||
HRESULT WINAPI WsCall(WS_SERVICE_PROXY*, const WS_OPERATION_DESCRIPTION*, const void**,
|
||||
WS_HEAP*, const WS_CALL_PROPERTY*, const ULONG, const WS_ASYNC_CONTEXT*,
|
||||
|
@ -1015,6 +1033,8 @@ HRESULT WINAPI WsCreateServiceProxyFromTemplate(WS_CHANNEL_TYPE, const WS_PROXY_
|
|||
HRESULT WINAPI WsCreateWriter(const WS_XML_WRITER_PROPERTY*, ULONG, WS_XML_WRITER**, WS_ERROR*);
|
||||
HRESULT WINAPI WsCreateXmlBuffer(WS_HEAP*, const WS_XML_BUFFER_PROPERTY*, ULONG, WS_XML_BUFFER**,
|
||||
WS_ERROR*);
|
||||
HRESULT WINAPI WsDateTimeToFileTime(const WS_DATETIME*, FILETIME*, WS_ERROR*);
|
||||
HRESULT WINAPI WsFileTimeToDateTime(const FILETIME*, WS_DATETIME*, WS_ERROR*);
|
||||
HRESULT WINAPI WsFillReader(WS_XML_READER*, ULONG, const WS_ASYNC_CONTEXT*, WS_ERROR*);
|
||||
HRESULT WINAPI WsFindAttribute(WS_XML_READER*, const WS_XML_STRING*, const WS_XML_STRING*, BOOL,
|
||||
ULONG*, WS_ERROR*);
|
||||
|
|
Loading…
Reference in New Issue