From e1cbec75c1383ef605d91c2c36841c360eb3cba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Zalewski?= Date: Fri, 11 May 2007 17:03:13 +0200 Subject: [PATCH] msxml3: Improve the XPath queries handling. --- dlls/msxml3/Makefile.in | 1 + dlls/msxml3/domdoc.c | 15 +- dlls/msxml3/msxml_private.h | 5 +- dlls/msxml3/node.c | 33 +--- dlls/msxml3/nodelist.c | 227 +++----------------------- dlls/msxml3/queryresult.c | 292 ++++++++++++++++++++++++++++++++++ dlls/msxml3/tests/Makefile.in | 2 +- dlls/msxml3/tests/domdoc.c | 250 +++++++++++++++++++++++++++++ 8 files changed, 584 insertions(+), 241 deletions(-) create mode 100644 dlls/msxml3/queryresult.c diff --git a/dlls/msxml3/Makefile.in b/dlls/msxml3/Makefile.in index 17f723f1a46..c888e236170 100644 --- a/dlls/msxml3/Makefile.in +++ b/dlls/msxml3/Makefile.in @@ -20,6 +20,7 @@ C_SRCS = \ nodemap.c \ parseerror.c \ pi.c \ + queryresult.c \ regsvr.c \ schema.c \ text.c \ diff --git a/dlls/msxml3/domdoc.c b/dlls/msxml3/domdoc.c index a0264ca8656..8f5f3cbc568 100644 --- a/dlls/msxml3/domdoc.c +++ b/dlls/msxml3/domdoc.c @@ -823,15 +823,18 @@ static HRESULT WINAPI domdoc_getElementsByTagName( IXMLDOMNodeList** resultList ) { domdoc *This = impl_from_IXMLDOMDocument2( iface ); - xmlChar *name; + LPWSTR szPattern; + HRESULT hr; TRACE("(%p)->(%s, %p)\n", This, debugstr_w(tagName), resultList); - name = xmlChar_from_wchar((WCHAR*)tagName); - *resultList = create_filtered_nodelist((xmlNodePtr)get_doc(This), name, TRUE); - HeapFree(GetProcessHeap(), 0, name); + szPattern = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(2+lstrlenW(tagName)+1)); + szPattern[0] = szPattern[1] = '/'; + lstrcpyW(szPattern + 2, tagName); - if(!*resultList) return S_FALSE; - return S_OK; + hr = queryresult_create((xmlNodePtr)get_doc(This), szPattern, resultList); + HeapFree(GetProcessHeap(), 0, szPattern); + + return hr; } static DOMNodeType get_node_type(VARIANT Type) diff --git a/dlls/msxml3/msxml_private.h b/dlls/msxml3/msxml_private.h index 4f459b9bd93..39aaa00eaf4 100644 --- a/dlls/msxml3/msxml_private.h +++ b/dlls/msxml3/msxml_private.h @@ -41,9 +41,10 @@ extern IUnknown *create_attribute( xmlNodePtr attribute ); extern IUnknown *create_text( xmlNodePtr text ); extern IUnknown *create_pi( xmlNodePtr pi ); extern IUnknown *create_comment( xmlNodePtr comment ); -extern IXMLDOMNodeList *create_nodelist( xmlNodePtr node ); +extern IXMLDOMNodeList *create_children_nodelist( xmlNodePtr ); extern IXMLDOMNamedNodeMap *create_nodemap( IXMLDOMNode *node ); -extern IXMLDOMNodeList *create_filtered_nodelist( xmlNodePtr, const xmlChar *, BOOL ); + +extern HRESULT queryresult_create( xmlNodePtr, LPWSTR, IXMLDOMNodeList ** ); extern void attach_xmlnode( IXMLDOMNode *node, xmlNodePtr xmlnode ); diff --git a/dlls/msxml3/node.c b/dlls/msxml3/node.c index 512e4e9e90f..8c48ecf9aba 100644 --- a/dlls/msxml3/node.c +++ b/dlls/msxml3/node.c @@ -311,23 +311,10 @@ static HRESULT WINAPI xmlnode_get_childNodes( if ( !childList ) return E_INVALIDARG; - switch(This->node->type) - { - case XML_ELEMENT_NODE: - *childList = create_filtered_nodelist( This->node->children, (const xmlChar *)"*", FALSE ); - break; + *childList = create_children_nodelist(This->node); + if (*childList == NULL) + return E_OUTOFMEMORY; - case XML_ATTRIBUTE_NODE: - *childList = create_filtered_nodelist( This->node->children, (const xmlChar *)"node()", FALSE ); - break; - - default: - FIXME("unhandled node type %d\n", This->node->type); - break; - } - - if (!*childList) - return S_FALSE; return S_OK; } @@ -658,21 +645,10 @@ static HRESULT WINAPI xmlnode_selectNodes( IXMLDOMNodeList** resultList) { xmlnode *This = impl_from_IXMLDOMNode( iface ); - xmlChar *str = NULL; - HRESULT r = E_FAIL; TRACE("%p %s %p\n", This, debugstr_w(queryString), resultList ); - str = xmlChar_from_wchar( queryString ); - if (!str) - return r; - - if( !This->node->children ) - return S_FALSE; - - *resultList = create_filtered_nodelist( This->node->children, str, FALSE ); - HeapFree( GetProcessHeap(), 0, str ); - return S_OK; + return queryresult_create( This->node, queryString, resultList ); } static HRESULT WINAPI xmlnode_selectSingleNode( @@ -686,6 +662,7 @@ static HRESULT WINAPI xmlnode_selectSingleNode( TRACE("%p %s %p\n", This, debugstr_w(queryString), resultNode ); + *resultNode = NULL; r = IXMLDOMNode_selectNodes(iface, queryString, &list); if(r == S_OK) { diff --git a/dlls/msxml3/nodelist.c b/dlls/msxml3/nodelist.c index a1b43060c12..9b1aa904833 100644 --- a/dlls/msxml3/nodelist.c +++ b/dlls/msxml3/nodelist.c @@ -33,147 +33,25 @@ #include "wine/debug.h" +/* This file implements the object returned by childNodes property. Note that this is + * not the IXMLDOMNodeList returned by XPath querites - it's implemented in queryresult.c. + * They are different because the list returned by childNodes: + * - is "live" - changes to the XML tree are automatically reflected in the list + * - doesn't supports IXMLDOMSelection + * - note that an attribute node have a text child in DOM but not in the XPath data model + * thus the child is inaccessible by an XPath query + */ + WINE_DEFAULT_DEBUG_CHANNEL(msxml); #ifdef HAVE_LIBXML2 -#ifdef HAVE_LIBXSLT - -#ifdef HAVE_LIBXSLT_PATTERN_H -#include -#endif -#ifdef HAVE_LIBXSLT_TRANSFORM_H -#include -#endif - -struct xslt_info { - xsltTransformContextPtr ctxt; - xsltCompMatchPtr pattern; - xsltStylesheetPtr sheet; -}; - -static void xslt_info_init( struct xslt_info *info ) -{ - info->ctxt = NULL; - info->pattern = NULL; - info->sheet = NULL; -} - -static int create_xslt_parser( struct xslt_info *info, xmlNodePtr node, const xmlChar *str ) -{ - if(!node) return 1; - - info->sheet = xsltNewStylesheet(); - if (!info->sheet) - return 0; - - info->ctxt = xsltNewTransformContext( info->sheet, node->doc ); - if (!info->ctxt) - return 0; - - info->pattern = xsltCompilePattern( str, node->doc, - node, info->sheet, info->ctxt ); - if (!info->pattern) - return 0; - return 1; -} - -static void free_xslt_info( struct xslt_info *info ) -{ - if (info->pattern) - xsltFreeCompMatchList( info->pattern ); - if (info->sheet) - xsltFreeStylesheet( info->sheet ); - if (info->ctxt) - xsltFreeTransformContext( info->ctxt ); -} - - -static xmlNodePtr get_next_node( struct xslt_info *info, xmlNodePtr node, xmlNodePtr *top_level_node ); - -static HRESULT xslt_next_match( struct xslt_info *info, xmlNodePtr *node, xmlNodePtr *top_level_node ) -{ - if (!info->ctxt) - return S_FALSE; - - /* make sure that the current element matches the pattern */ - while ( *node ) - { - int r; - - r = xsltTestCompMatchList( info->ctxt, *node, info->pattern ); - if ( 1 == r ) - { - TRACE("Matched %p (%s)\n", *node, (*node)->name ); - return S_OK; - } - if (r != 0) - { - ERR("Pattern match failed\n"); - return E_FAIL; - } - *node = get_next_node(info, *node, top_level_node); - } - return S_OK; -} - -#else - -struct xslt_info { - /* empty */ -}; - -static void xslt_info_init( struct xslt_info *info ) -{ -} - -void free_xslt_info( struct xslt_info *info ) -{ -} - -static int create_xslt_parser( struct xslt_info *info, xmlNodePtr node, const xmlChar *str ) -{ - MESSAGE("libxslt was missing at compile time\n"); - return 0; -} - -static HRESULT xslt_next_match( struct xslt_info *info, xmlNodePtr *node, xmlNodePtr *top_level_node ) -{ - return S_FALSE; -} - -#endif - -static xmlNodePtr get_next_node( struct xslt_info *info, xmlNodePtr node, xmlNodePtr *top_level_node ) -{ - if(!top_level_node) return node->next; - - if(node->children) return node->children; - if(node->next) - { - if(node == *top_level_node) - *top_level_node = node->next; - return node->next; - } - - if(node != *top_level_node && node->parent) - { - if(node->parent == *top_level_node) - *top_level_node = node->parent->next; - return node->parent->next; - } - return NULL; -} - typedef struct _xmlnodelist { const struct IXMLDOMNodeListVtbl *lpVtbl; LONG ref; - xmlNodePtr node; + xmlNodePtr parent; xmlNodePtr current; - xmlNodePtr top_level_node; - BOOL enum_children; - struct xslt_info xinfo; } xmlnodelist; static inline xmlnodelist *impl_from_IXMLDOMNodeList( IXMLDOMNodeList *iface ) @@ -222,8 +100,7 @@ static ULONG WINAPI xmlnodelist_Release( ref = InterlockedDecrement( &This->ref ); if ( ref == 0 ) { - free_xslt_info( &This->xinfo ); - if(This->node) xmldoc_release( This->node->doc ); + xmldoc_release( This->parent->doc ); HeapFree( GetProcessHeap(), 0, This ); } @@ -281,10 +158,8 @@ static HRESULT WINAPI xmlnodelist_get_item( IXMLDOMNode** listItem) { xmlnodelist *This = impl_from_IXMLDOMNodeList( iface ); - xmlNodePtr curr, tmp; - xmlNodePtr *top_level_node = NULL; + xmlNodePtr curr; long nodeIndex = 0; - HRESULT r; TRACE("%p %ld\n", This, index); @@ -293,20 +168,11 @@ static HRESULT WINAPI xmlnodelist_get_item( if (index < 0) return S_FALSE; - curr = This->node; - - if(This->enum_children) - { - tmp = curr; - top_level_node = &tmp; - } - + curr = This->parent->children; while(curr) { - r = xslt_next_match( &This->xinfo, &curr, top_level_node); - if(FAILED(r) || !curr) return S_FALSE; if(nodeIndex++ == index) break; - curr = get_next_node(&This->xinfo, curr, top_level_node); + curr = curr->next; } if(!curr) return S_FALSE; @@ -320,34 +186,18 @@ static HRESULT WINAPI xmlnodelist_get_length( long* listLength) { - xmlNodePtr curr, tmp; - xmlNodePtr *top_level_node = NULL; + xmlNodePtr curr; long nodeCount = 0; - HRESULT r; xmlnodelist *This = impl_from_IXMLDOMNodeList( iface ); TRACE("%p\n", This); - if (This->node == NULL) { - *listLength = 0; - return S_OK; - } - - curr = This->node; - - if(This->enum_children) - { - tmp = curr; - top_level_node = &tmp; - } - + curr = This->parent->children; while (curr) { - r = xslt_next_match( &This->xinfo, &curr, top_level_node ); - if(FAILED(r) || !curr) break; nodeCount++; - curr = get_next_node(&This->xinfo, curr, top_level_node); + curr = curr->next; } *listLength = nodeCount; @@ -359,25 +209,16 @@ static HRESULT WINAPI xmlnodelist_nextNode( IXMLDOMNode** nextItem) { xmlnodelist *This = impl_from_IXMLDOMNodeList( iface ); - HRESULT r; - xmlNodePtr *top_level_node = NULL; TRACE("%p %p\n", This, nextItem ); *nextItem = NULL; - if(This->enum_children) - top_level_node = &This->top_level_node; - - r = xslt_next_match( &This->xinfo, &This->current, top_level_node ); - if (FAILED(r) ) - return r; - if (!This->current) return S_FALSE; *nextItem = create_node( This->current ); - This->current = get_next_node(&This->xinfo, This->current, top_level_node); + This->current = This->current->next; return S_OK; } @@ -387,7 +228,7 @@ static HRESULT WINAPI xmlnodelist_reset( xmlnodelist *This = impl_from_IXMLDOMNodeList( iface ); TRACE("%p\n", This); - This->current = This->node; + This->current = This->parent->children; return S_OK; } @@ -416,7 +257,7 @@ static const struct IXMLDOMNodeListVtbl xmlnodelist_vtbl = xmlnodelist__newEnum, }; -static xmlnodelist *new_nodelist( xmlNodePtr node ) +IXMLDOMNodeList* create_children_nodelist( xmlNodePtr node ) { xmlnodelist *nodelist; @@ -426,34 +267,12 @@ static xmlnodelist *new_nodelist( xmlNodePtr node ) nodelist->lpVtbl = &xmlnodelist_vtbl; nodelist->ref = 1; - nodelist->node = node; - nodelist->current = node; - nodelist->top_level_node = node; - nodelist->enum_children = FALSE; - xslt_info_init( &nodelist->xinfo ); + nodelist->parent = node; + nodelist->current = node->children; - if(node) xmldoc_add_ref( node->doc ); + xmldoc_add_ref( node->doc ); - return nodelist; -} - -IXMLDOMNodeList* create_nodelist( xmlNodePtr node ) -{ - xmlnodelist *nodelist = new_nodelist( node ); return (IXMLDOMNodeList*) &nodelist->lpVtbl; } -IXMLDOMNodeList* create_filtered_nodelist( xmlNodePtr node, const xmlChar *str, BOOL enum_children ) -{ - xmlnodelist *This = new_nodelist( node ); - if (create_xslt_parser( &This->xinfo, node, str )) - { - This->enum_children = enum_children; - return (IXMLDOMNodeList*) &This->lpVtbl; - } - - IXMLDOMNodeList_Release( (IXMLDOMNodeList*) &This->lpVtbl ); - return NULL; -} - #endif diff --git a/dlls/msxml3/queryresult.c b/dlls/msxml3/queryresult.c new file mode 100644 index 00000000000..4e9c4096de6 --- /dev/null +++ b/dlls/msxml3/queryresult.c @@ -0,0 +1,292 @@ +/* + * XPath query result node list implementation (TODO: XSLPattern support) + * + * Copyright 2005 Mike McCormack + * Copyright 2007 Mikolaj Zalewski + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS + +#include "config.h" + +#include +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "ole2.h" +#include "msxml2.h" + +#include "msxml_private.h" + +#include "wine/debug.h" + +/* This file implements the object returned by a XPath query. Note that this is + * not the IXMLDOMNodeList returned by childNodes - it's implemented in nodelist.c. + * They are different because the list returned by XPath queries: + * - is static - gives the results for the XML tree as it existed during the + * execution of the query + * - supports IXMLDOMSelection (TODO) + * + * TODO: XSLPattern support + */ + +WINE_DEFAULT_DEBUG_CHANNEL(msxml); + +#ifdef HAVE_LIBXML2 + +#include + +static const struct IXMLDOMNodeListVtbl queryresult_vtbl; + +typedef struct _queryresult +{ + const struct IXMLDOMNodeListVtbl *lpVtbl; + LONG ref; + xmlNodePtr node; + xmlXPathObjectPtr result; + int resultPos; +} queryresult; + +static inline queryresult *impl_from_IXMLDOMNodeList( IXMLDOMNodeList *iface ) +{ + return (queryresult *)((char*)iface - FIELD_OFFSET(queryresult, lpVtbl)); +} + +HRESULT queryresult_create(xmlNodePtr node, LPWSTR szQuery, IXMLDOMNodeList **out) +{ + queryresult *This = CoTaskMemAlloc(sizeof(queryresult)); + xmlXPathContextPtr ctxt = xmlXPathNewContext(node->doc); + xmlChar *str = xmlChar_from_wchar(szQuery); + HRESULT hr; + + + TRACE("(%p, %s, %p)\n", node, wine_dbgstr_w(szQuery), out); + + *out = NULL; + if (This == NULL || ctxt == NULL || str == NULL) + { + hr = E_OUTOFMEMORY; + goto cleanup; + } + + This->lpVtbl = &queryresult_vtbl; + This->ref = 1; + This->resultPos = 0; + This->node = node; + xmldoc_add_ref(This->node->doc); + + ctxt->node = node; + This->result = xmlXPathEval(str, ctxt); + if (!This->result || This->result->type != XPATH_NODESET) + { + hr = E_FAIL; + goto cleanup; + } + + *out = (IXMLDOMNodeList *)This; + hr = S_OK; + TRACE("found %d matches\n", This->result->nodesetval->nodeNr); + +cleanup: + if (This != NULL && FAILED(hr)) + IXMLDOMNodeList_Release( (IXMLDOMNodeList*) &This->lpVtbl ); + if (ctxt != NULL) + xmlXPathFreeContext(ctxt); + HeapFree(GetProcessHeap(), 0, str); + return hr; +} + + +static HRESULT WINAPI queryresult_QueryInterface( + IXMLDOMNodeList *iface, + REFIID riid, + void** ppvObject ) +{ + TRACE("%p %s %p\n", iface, debugstr_guid(riid), ppvObject); + + if ( IsEqualGUID( riid, &IID_IUnknown ) || + IsEqualGUID( riid, &IID_IDispatch ) || + IsEqualGUID( riid, &IID_IXMLDOMNodeList ) ) + { + *ppvObject = iface; + } + else + { + FIXME("interface %s not implemented\n", debugstr_guid(riid)); + *ppvObject = NULL; + return E_NOINTERFACE; + } + + IXMLDOMNodeList_AddRef( iface ); + + return S_OK; +} + +static ULONG WINAPI queryresult_AddRef( + IXMLDOMNodeList *iface ) +{ + queryresult *This = impl_from_IXMLDOMNodeList( iface ); + return InterlockedIncrement( &This->ref ); +} + +static ULONG WINAPI queryresult_Release( + IXMLDOMNodeList *iface ) +{ + queryresult *This = impl_from_IXMLDOMNodeList( iface ); + ULONG ref; + + ref = InterlockedDecrement(&This->ref); + if ( ref == 0 ) + { + xmlXPathFreeObject(This->result); + xmldoc_release(This->node->doc); + CoTaskMemFree(This); + } + + return ref; +} + +static HRESULT WINAPI queryresult_GetTypeInfoCount( + IXMLDOMNodeList *iface, + UINT* pctinfo ) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI queryresult_GetTypeInfo( + IXMLDOMNodeList *iface, + UINT iTInfo, + LCID lcid, + ITypeInfo** ppTInfo ) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI queryresult_GetIDsOfNames( + IXMLDOMNodeList *iface, + REFIID riid, + LPOLESTR* rgszNames, + UINT cNames, + LCID lcid, + DISPID* rgDispId ) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI queryresult_Invoke( + IXMLDOMNodeList *iface, + DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + UINT* puArgErr ) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI queryresult_get_item( + IXMLDOMNodeList* iface, + long index, + IXMLDOMNode** listItem) +{ + queryresult *This = impl_from_IXMLDOMNodeList( iface ); + + TRACE("%p %ld\n", This, index); + + *listItem = NULL; + + if (index < 0 || index >= This->result->nodesetval->nodeNr) + return S_FALSE; + + *listItem = create_node(This->result->nodesetval->nodeTab[index]); + + return S_OK; +} + +static HRESULT WINAPI queryresult_get_length( + IXMLDOMNodeList* iface, + long* listLength) +{ + queryresult *This = impl_from_IXMLDOMNodeList( iface ); + + TRACE("%p\n", This); + + *listLength = This->result->nodesetval->nodeNr; + return S_OK; +} + +static HRESULT WINAPI queryresult_nextNode( + IXMLDOMNodeList* iface, + IXMLDOMNode** nextItem) +{ + queryresult *This = impl_from_IXMLDOMNodeList( iface ); + + TRACE("%p %p\n", This, nextItem ); + + *nextItem = NULL; + + if (This->resultPos >= This->result->nodesetval->nodeNr) + return S_FALSE; + + *nextItem = create_node(This->result->nodesetval->nodeTab[This->resultPos]); + This->resultPos++; + return S_OK; +} + +static HRESULT WINAPI queryresult_reset( + IXMLDOMNodeList* iface) +{ + queryresult *This = impl_from_IXMLDOMNodeList( iface ); + + TRACE("%p\n", This); + This->resultPos = 0; + return S_OK; +} + +static HRESULT WINAPI queryresult__newEnum( + IXMLDOMNodeList* iface, + IUnknown** ppUnk) +{ + FIXME("\n"); + return E_NOTIMPL; +} + + +static const struct IXMLDOMNodeListVtbl queryresult_vtbl = +{ + queryresult_QueryInterface, + queryresult_AddRef, + queryresult_Release, + queryresult_GetTypeInfoCount, + queryresult_GetTypeInfo, + queryresult_GetIDsOfNames, + queryresult_Invoke, + queryresult_get_item, + queryresult_get_length, + queryresult_nextNode, + queryresult_reset, + queryresult__newEnum, +}; + +#endif diff --git a/dlls/msxml3/tests/Makefile.in b/dlls/msxml3/tests/Makefile.in index 2aabdb6efa4..e1131b81a90 100644 --- a/dlls/msxml3/tests/Makefile.in +++ b/dlls/msxml3/tests/Makefile.in @@ -3,7 +3,7 @@ TOPOBJDIR = ../../.. SRCDIR = @srcdir@ VPATH = @srcdir@ TESTDLL = msxml3.dll -IMPORTS = oleaut32 ole32 kernel32 +IMPORTS = oleaut32 ole32 user32 kernel32 EXTRALIBS = -luuid CTESTS = \ diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c index 02e8bf99d75..2d9ff5c8860 100644 --- a/dlls/msxml3/tests/domdoc.c +++ b/dlls/msxml3/tests/domdoc.c @@ -85,6 +85,39 @@ static const WCHAR szComplete5[] = { '<','/','S',':','s','e','a','r','c','h','>',0 }; +static const CHAR szExampleXML[] = +"\n" +"\n" +" \n" +" A1 field\n" +" B1 field\n" +" C1 field\n" +" \n" +" \n" +" This is a description. \n" +" \n" +" \n" +" \n" +"\n" +" \n" +" A2 field\n" +" B2 field\n" +" C2 field\n" +" \n" +"\n" +" \n" +" A3 field\n" +" B3 field\n" +" C3 field\n" +" \n" +"\n" +" \n" +" A4 field\n" +" B4 field\n" +" C4 field\n" +" \n" +"\n"; + static const WCHAR szNonExistentFile[] = { 'c', ':', '\\', 'N', 'o', 'n', 'e', 'x', 'i', 's', 't', 'e', 'n', 't', '.', 'x', 'm', 'l', 0 }; @@ -155,6 +188,142 @@ static VARIANT _variantbstr_(const char *str) return v; } +static void get_str_for_type(DOMNodeType type, char *buf) +{ + switch (type) + { + case NODE_ATTRIBUTE: + strcpy(buf, "A"); + break; + case NODE_ELEMENT: + strcpy(buf, "E"); + break; + case NODE_DOCUMENT: + strcpy(buf, "D"); + break; + default: + wsprintfA(buf, "[%d]", type); + } +} + +static int get_node_position(IXMLDOMNode *node) +{ + HRESULT r; + int pos = 0; + + IXMLDOMNode_AddRef(node); + do + { + IXMLDOMNode *new_node; + + pos++; + r = IXMLDOMNode_get_previousSibling(node, &new_node); + ok(!FAILED(r), "get_previousSibling failed\n"); + IXMLDOMNode_Release(node); + node = new_node; + } while (r == S_OK); + return pos; +} + +static void node_to_string(IXMLDOMNode *node, char *buf) +{ + HRESULT r = S_OK; + DOMNodeType type; + + if (node == NULL) + { + lstrcpyA(buf, "(null)"); + return; + } + + IXMLDOMNode_AddRef(node); + while (r == S_OK) + { + IXMLDOMNode *new_node; + + ole_check(IXMLDOMNode_get_nodeType(node, &type)); + get_str_for_type(type, buf); + buf+=strlen(buf); + + if (type == NODE_ATTRIBUTE) + { + BSTR bstr; + ole_check(IXMLDOMNode_get_nodeName(node, &bstr)); + *(buf++) = '\''; + wsprintfA(buf, "%ws", bstr); + buf += strlen(buf); + *(buf++) = '\''; + SysFreeString(bstr); + + r = IXMLDOMNode_selectSingleNode(node, _bstr_(".."), &new_node); + } + else + { + int pos = get_node_position(node); + DOMNodeType parent_type = NODE_INVALID; + r = IXMLDOMNode_get_parentNode(node, &new_node); + + /* currently wine doesn't create a node for the . To be able to test query + * results we "fix" it */ + if (r == S_OK) + ole_check(IXMLDOMNode_get_nodeType(node, &parent_type)); + /* we need also to workaround the no document node problem - see below */ + if (((r == S_FALSE && type != NODE_DOCUMENT) || parent_type == NODE_DOCUMENT) && type != NODE_PROCESSING_INSTRUCTION && pos==1) + { + todo_wine ok(FALSE, "The first child of the document node in MSXML is the processing instruction\n"); + pos++; + } + wsprintf(buf, "%d", pos); + buf += strlen(buf); + } + + ok(!FAILED(r), "get_parentNode failed (%08x)\n", r); + IXMLDOMNode_Release(node); + node = new_node; + if (r == S_OK) + *(buf++) = '.'; + } + + /* currently we can't access document node in wine. All our examples this is the + * root node so to be able to test query results we add it */ + if (type != NODE_DOCUMENT) + { + todo_wine ok(FALSE, "Document node is not the last returned node!\n"); + *(buf++) = '.'; + *(buf++) = 'D'; + *(buf++) = '1'; + } + *buf = 0; +} + +static char *list_to_string(IXMLDOMNodeList *list) +{ + static char buf[4096]; + char *pos = buf; + long len = 0; + int i; + + if (list == NULL) + { + lstrcpyA(buf, "(null)"); + return buf; + } + ole_check(IXMLDOMNodeList_get_length(list, &len)); + for (i = 0; i < len; i++) + { + IXMLDOMNode *node; + if (i > 0) + *(pos++) = ' '; + ole_check(IXMLDOMNodeList_nextNode(list, &node)); + node_to_string(node, pos); + pos += strlen(pos); + IXMLDOMNode_Release(node); + } + *pos = 0; + return buf; +} +#define expect_list_and_release(list, expstr) { char *str = list_to_string(list); ok(strcmp(str, expstr)==0, "Invalid node list: %s, exptected %s\n", str, expstr); if (list) IXMLDOMNodeList_Release(list); } + static void test_domdoc( void ) { HRESULT r; @@ -1272,6 +1441,86 @@ static void test_IXMLDOMDocument2(void) free_bstrs(); } +static void test_XPath() +{ + HRESULT r; + VARIANT_BOOL b; + IXMLDOMDocument2 *doc; + IXMLDOMNode *rootNode; + IXMLDOMNode *elem1Node; + IXMLDOMNodeList *list; + + r = CoCreateInstance( &CLSID_DOMDocument, NULL, + CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument2, (LPVOID*)&doc ); + if( r != S_OK ) + return; + + ole_check(IXMLDOMDocument_loadXML(doc, _bstr_(szExampleXML), &b)); + ok(b == VARIANT_TRUE, "failed to load XML string\n"); + + /* switch to XPath */ + ole_check(IXMLDOMDocument2_setProperty(doc, _bstr_("SelectionLanguage"), _variantbstr_("XPath"))); + + /* some simple queries*/ + ole_check(IXMLDOMDocument_selectNodes(doc, _bstr_("root"), &list)); + ole_check(IXMLDOMNodeList_get_item(list, 0, &rootNode)); + ole_check(IXMLDOMNodeList_reset(list)); + expect_list_and_release(list, "E2.D1"); + if (rootNode == NULL) + return; + + ole_check(IXMLDOMDocument_selectNodes(doc, _bstr_("root//c"), &list)); + expect_list_and_release(list, "E3.E1.E2.D1 E3.E2.E2.D1"); + + ole_check(IXMLDOMDocument_selectNodes(doc, _bstr_("//c[@type]"), &list)); + expect_list_and_release(list, "E3.E2.E2.D1"); + + ole_check(IXMLDOMNode_selectNodes(rootNode, _bstr_("elem"), &list)); + expect_list_and_release(list, "E1.E2.D1 E2.E2.D1 E4.E2.D1"); + + ole_check(IXMLDOMNode_selectNodes(rootNode, _bstr_("."), &list)); + expect_list_and_release(list, "E2.D1"); + + ole_check(IXMLDOMNode_selectNodes(rootNode, _bstr_("elem[3]/preceding-sibling::*"), &list)); + ole_check(IXMLDOMNodeList_get_item(list, 0, &elem1Node)); + ole_check(IXMLDOMNodeList_reset(list)); + expect_list_and_release(list, "E1.E2.D1 E2.E2.D1 E3.E2.D1"); + + /* select an attribute */ + ole_check(IXMLDOMNode_selectNodes(rootNode, _bstr_(".//@type"), &list)); + expect_list_and_release(list, "A'type'.E3.E2.E2.D1"); + + /* would evaluate to a number */ + ole_expect(IXMLDOMNode_selectNodes(rootNode, _bstr_("count(*)"), &list), E_FAIL); + /* would evaluate to a boolean */ + ole_expect(IXMLDOMNode_selectNodes(rootNode, _bstr_("position()>0"), &list), E_FAIL); + /* would evaluate to a string */ + ole_expect(IXMLDOMNode_selectNodes(rootNode, _bstr_("name()"), &list), E_FAIL); + + /* no results */ + ole_check(IXMLDOMNode_selectNodes(rootNode, _bstr_("c"), &list)); + expect_list_and_release(list, ""); + ole_check(IXMLDOMDocument_selectNodes(doc, _bstr_("elem//c"), &list)); + expect_list_and_release(list, ""); + ole_check(IXMLDOMDocument_selectNodes(doc, _bstr_("//elem[4]"), &list)); + expect_list_and_release(list, ""); + + /* foo undeclared in document node */ + ole_expect(IXMLDOMDocument_selectNodes(doc, _bstr_("root//foo:c"), &list), E_FAIL); + /* undeclared in node */ + ole_expect(IXMLDOMNode_selectNodes(rootNode, _bstr_(".//foo:c"), &list), E_FAIL); + /* undeclared in node */ + ole_expect(IXMLDOMNode_selectNodes(elem1Node, _bstr_("//foo:c"), &list), E_FAIL); + /* but this trick can be used */ + ole_check(IXMLDOMNode_selectNodes(elem1Node, _bstr_("//*[name()='foo:c']"), &list)); + expect_list_and_release(list, "E3.E4.E2.D1"); + + IXMLDOMNode_Release(rootNode); + IXMLDOMNode_Release(elem1Node); + IXMLDOMDocument_Release(doc); + free_bstrs(); +} + START_TEST(domdoc) { HRESULT r; @@ -1289,6 +1538,7 @@ START_TEST(domdoc) test_removeChild(); test_XMLHTTP(); test_IXMLDOMDocument2(); + test_XPath(); CoUninitialize(); }