msxml3: improve the XPath queries handling (resend)
Mikołaj Zalewski
mikolaj at zalewski.pl
Thu May 17 08:33:36 CDT 2007
This version features an updated comment in queryresult.c
-------------- next part --------------
From c26a8e04f8cec47e37e4ca4e4a843473c2ae550e Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Miko=C5=82aj_Zalewski?= <mikolaj at zalewski.pl>
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(-)
diff --git a/dlls/msxml3/Makefile.in b/dlls/msxml3/Makefile.in
index 17f723f..c888e23 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 6954f81..347605b 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 4f459b9..39aaa00 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 512e4e9..8c48ecf 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 a1b4306..d2346f4 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 xpathresult.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 <libxslt/pattern.h>
-#endif
-#ifdef HAVE_LIBXSLT_TRANSFORM_H
-#include <libxslt/transform.h>
-#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 0000000..75ca2c8
--- /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 <stdarg.h>
+#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 <libxml/xpath.h>
+
+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 2aabdb6..e1131b8 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 02e8bf9..2cee379 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[] =
+"<?xml version='1.0' encoding='utf-8'?>\n"
+"<root xmlns:foo='urn:uuid:86B2F87F-ACB6-45cd-8B77-9BDB92A01A29'>\n"
+" <elem>\n"
+" <a>A1 field</a>\n"
+" <b>B1 field</b>\n"
+" <c>C1 field</c>\n"
+" <description xmlns:foo='http://www.winehq.org' xmlns:bar='urn:uuid:86B2F87F-ACB6-45cd-8B77-9BDB92A01A29'>\n"
+" <html xmlns='http://www.w3.org/1999/xhtml'>\n"
+" This is <strong>a</strong> <i>description</i>. <bar:x/>\n"
+" </html>\n"
+" </description>\n"
+" </elem>\n"
+"\n"
+" <elem>\n"
+" <a>A2 field</a>\n"
+" <b>B2 field</b>\n"
+" <c type=\"old\">C2 field</c>\n"
+" </elem>\n"
+"\n"
+" <elem xmlns='urn:uuid:86B2F87F-ACB6-45cd-8B77-9BDB92A01A29'>\n"
+" <a>A3 field</a>\n"
+" <b>B3 field</b>\n"
+" <c>C3 field</c>\n"
+" </elem>\n"
+"\n"
+" <elem>\n"
+" <a>A4 field</a>\n"
+" <b>B4 field</b>\n"
+" <foo:c>C4 field</foo:c>\n"
+" </elem>\n"
+"</root>\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 <?xml ... ?>. 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 <?xml ... ?> 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 <root> node */
+ ole_expect(IXMLDOMNode_selectNodes(rootNode, _bstr_(".//foo:c"), &list), E_FAIL);
+ /* undeclared in <elem> 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();
}
--
1.4.4.2
More information about the wine-patches
mailing list