1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: xpathapi.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 #include "xpathapi.hxx"
32 #include "nodelist.hxx"
33 #include "xpathobject.hxx"
34 #include "../dom/node.hxx"
36 #include <rtl/ustrbuf.hxx>
38 #include <libxml/xmlerror.h>
39 #include <libxml/xpath.h>
40 #include <libxml/xpathInternals.h>
49 Reference
< XInterface
> CXPathAPI::_getInstance(const Reference
< XMultiServiceFactory
>& rSMgr
)
51 return Reference
< XInterface
>(static_cast<XXPathAPI
*>(new CXPathAPI(rSMgr
)));
55 CXPathAPI::CXPathAPI(const Reference
< XMultiServiceFactory
>& rSMgr
)
60 const char* CXPathAPI::aImplementationName
= "com.sun.star.comp.xml.xpath.XPathAPI";
61 const char* CXPathAPI::aSupportedServiceNames
[] = {
62 "com.sun.star.xml.xpath.XPathAPI",
66 OUString
CXPathAPI::_getImplementationName()
68 return OUString::createFromAscii(aImplementationName
);
71 Sequence
<OUString
> CXPathAPI::_getSupportedServiceNames()
73 Sequence
<OUString
> aSequence
;
74 for (int i
=0; aSupportedServiceNames
[i
]!=NULL
; i
++) {
75 aSequence
.realloc(i
+1);
76 aSequence
[i
]=(OUString::createFromAscii(aSupportedServiceNames
[i
]));
81 Sequence
< OUString
> SAL_CALL
CXPathAPI::getSupportedServiceNames()
82 throw (RuntimeException
)
84 return CXPathAPI::_getSupportedServiceNames();
87 OUString SAL_CALL
CXPathAPI::getImplementationName()
88 throw (RuntimeException
)
90 return CXPathAPI::_getImplementationName();
93 sal_Bool SAL_CALL
CXPathAPI::supportsService(const OUString
& aServiceName
)
94 throw (RuntimeException
)
96 Sequence
< OUString
> supported
= CXPathAPI::_getSupportedServiceNames();
97 for (sal_Int32 i
=0; i
<supported
.getLength(); i
++)
99 if (supported
[i
] == aServiceName
) return sal_True
;
104 // -------------------------------------------------------------------
106 void SAL_CALL
CXPathAPI::registerNS(
107 const OUString
& aPrefix
,
108 const OUString
& aURI
)
109 throw (RuntimeException
)
111 m_nsmap
.insert(nsmap_t::value_type(aPrefix
, aURI
));
114 void SAL_CALL
CXPathAPI::unregisterNS(
115 const OUString
& aPrefix
,
116 const OUString
& aURI
)
117 throw (RuntimeException
)
119 if ((m_nsmap
.find(aPrefix
))->second
.equals(aURI
))
120 m_nsmap
.erase(aPrefix
);
123 // register all namespaces stored in the namespace list for this object
124 // with the current xpath evaluation context
125 static void _registerNamespaces(
126 xmlXPathContextPtr ctx
,
127 const nsmap_t
& nsmap
)
129 nsmap_t::const_iterator i
= nsmap
.begin();
130 OString oprefix
, ouri
;
132 while (i
!= nsmap
.end())
134 oprefix
= OUStringToOString(i
->first
, RTL_TEXTENCODING_UTF8
);
135 ouri
= OUStringToOString(i
->second
, RTL_TEXTENCODING_UTF8
);
136 p
= (xmlChar
*)oprefix
.getStr();
137 u
= (xmlChar
*)ouri
.getStr();
138 xmlXPathRegisterNs(ctx
, p
, u
);
143 // get all ns decls on a node (and parent nodes, if any) and register them
144 static void _collectNamespaces(
146 const Reference
< XNode
>& namespaceNode
)
148 // get namespace decls from node...
149 xmlNodePtr pNode
= DOM::CNode::getNodePtr(namespaceNode
);
151 xmlNsPtr curDef
= pNode
->nsDef
;
152 while (curDef
!= 0) {
153 const xmlChar
* xHref
= curDef
->href
;
154 OUString
aURI((sal_Char
*)xHref
, strlen((char*)xHref
), RTL_TEXTENCODING_UTF8
);
155 const xmlChar
* xPre
= curDef
->prefix
;
156 OUString
aPrefix((sal_Char
*)xPre
, strlen((char*)xPre
), RTL_TEXTENCODING_UTF8
);
157 pAPI
->registerNS(aPrefix
, aURI
);
158 curDef
= curDef
->next
;
160 pNode
= pNode
->parent
;
164 // register function and variable lookup functions with the current
165 // xpath evaluation context
166 static void _registerExtensions(
167 xmlXPathContextPtr ctx
,
168 const extensions_t
& extensions
)
170 extensions_t::const_iterator i
= extensions
.begin();
171 while (i
!= extensions
.end())
173 Libxml2ExtensionHandle aHandle
= (*i
)->getLibxml2ExtensionHandle();
174 if ( aHandle
.functionLookupFunction
!= 0 )
176 xmlXPathRegisterFuncLookup(ctx
,
177 reinterpret_cast<xmlXPathFuncLookupFunc
>(
178 sal::static_int_cast
<sal_IntPtr
>(aHandle
.functionLookupFunction
)),
179 reinterpret_cast<void*>(
180 sal::static_int_cast
<sal_IntPtr
>(aHandle
.functionData
)));
182 if ( aHandle
.variableLookupFunction
!= 0 )
184 xmlXPathRegisterVariableLookup(ctx
,
185 reinterpret_cast<xmlXPathVariableLookupFunc
>(
186 sal::static_int_cast
<sal_IntPtr
>(aHandle
.variableLookupFunction
)),
187 reinterpret_cast<void*>(
188 sal::static_int_cast
<sal_IntPtr
>(aHandle
.variableData
)));
195 * Use an XPath string to select a nodelist.
197 Reference
< XNodeList
> SAL_CALL
CXPathAPI::selectNodeList(
198 const Reference
< XNode
>& contextNode
,
199 const OUString
& expr
)
200 throw (RuntimeException
, XPathException
)
202 Reference
< XXPathObject
> xobj
= eval(contextNode
, expr
);
203 return xobj
->getNodeList();
207 * same as selectNodeList but registers all name space decalratiosn found on namespaceNode
209 Reference
< XNodeList
> SAL_CALL
CXPathAPI::selectNodeListNS(
210 const Reference
< XNode
>& contextNode
,
211 const OUString
& expr
,
212 const Reference
< XNode
>& namespaceNode
)
213 throw (RuntimeException
, XPathException
)
215 _collectNamespaces(this, namespaceNode
);
216 return selectNodeList(contextNode
, expr
);
220 * Same as selectNodeList but returns the first node (if any)
222 Reference
< XNode
> SAL_CALL
CXPathAPI::selectSingleNode(
223 const Reference
< XNode
>& contextNode
,
224 const OUString
& expr
)
225 throw (RuntimeException
, XPathException
)
227 Reference
< XNodeList
> aList
= selectNodeList(contextNode
, expr
);
228 Reference
< XNode
> aNode
= aList
->item(0);
233 * Same as selectSingleNode but registers all namespaces declared on
236 Reference
< XNode
> SAL_CALL
CXPathAPI::selectSingleNodeNS(
237 const Reference
< XNode
>& contextNode
,
238 const OUString
& expr
,
239 const Reference
< XNode
>& namespaceNode
)
240 throw (RuntimeException
, XPathException
)
242 _collectNamespaces(this, namespaceNode
);
243 return selectSingleNode(contextNode
, expr
);
246 static OUString
make_error_message(xmlErrorPtr pError
)
248 ::rtl::OUStringBuffer buf
;
249 if (pError
->message
) {
250 buf
.appendAscii(pError
->message
);
252 int line
= pError
->line
;
254 buf
.appendAscii("Line: ");
255 buf
.append(static_cast<sal_Int32
>(line
));
256 buf
.appendAscii("\n");
258 int column
= pError
->int2
;
260 buf
.appendAscii("Column: ");
261 buf
.append(static_cast<sal_Int32
>(column
));
262 buf
.appendAscii("\n");
264 OUString msg
= buf
.makeStringAndClear();
270 static void generic_error_func(void *userData
, const char *format
, ...)
276 va_start(args
, format
);
278 #define vsnprintf _vsnprintf
280 vsnprintf(str
, sizeof(str
), format
, args
);
283 ::rtl::OUStringBuffer
buf(
284 OUString::createFromAscii("libxml2 error:\n"));
285 buf
.appendAscii(str
);
286 OString msg
= OUStringToOString(buf
.makeStringAndClear(),
287 RTL_TEXTENCODING_ASCII_US
);
288 OSL_ENSURE(sal_False
, msg
.getStr());
291 static void structured_error_func(void * userData
, xmlErrorPtr error
)
294 ::rtl::OUStringBuffer
buf(
295 OUString::createFromAscii("libxml2 error:\n"));
297 buf
.append(make_error_message(error
));
299 buf
.append(OUString::createFromAscii("no error argument!"));
301 OString msg
= OUStringToOString(buf
.makeStringAndClear(),
302 RTL_TEXTENCODING_ASCII_US
);
303 OSL_ENSURE(sal_False
, msg
.getStr());
309 * evaluates an XPath string. relative XPath expressions are evaluated relative to
312 Reference
< XXPathObject
> SAL_CALL
CXPathAPI::eval(
313 const Reference
< XNode
>& contextNode
,
314 const OUString
& expr
)
315 throw (RuntimeException
, XPathException
)
317 xmlXPathContextPtr xpathCtx
;
318 xmlXPathObjectPtr xpathObj
;
320 // get the node and document
321 xmlNodePtr pNode
= DOM::CNode::getNodePtr(contextNode
);
322 xmlDocPtr pDoc
= pNode
->doc
;
324 /* NB: workaround for #i87252#:
325 libxml < 2.6.17 considers it an error if the context
326 node is the empty document (i.e. its xpathCtx->doc has no
327 children). libxml 2.6.17 does not consider it an error.
328 Unfortunately, old libxml prints an error message to stderr,
329 which (afaik) cannot be turned off in this case, so we handle it.
331 if (!pDoc
->children
) {
332 throw XPathException();
335 /* Create xpath evaluation context */
336 xpathCtx
= xmlXPathNewContext(pDoc
);
337 if (xpathCtx
== NULL
) throw XPathException();
340 xpathCtx
->node
= pNode
;
342 xpathCtx
->error
= structured_error_func
;
343 xmlSetGenericErrorFunc(NULL
, generic_error_func
);
345 // register namespaces and extension
346 _registerNamespaces(xpathCtx
, m_nsmap
);
347 _registerExtensions(xpathCtx
, m_extensions
);
350 OString o1
= OUStringToOString(expr
, RTL_TEXTENCODING_UTF8
);
351 xmlChar
*xStr
= (xmlChar
*)o1
.getStr();
352 if ((xpathObj
= xmlXPathEval(xStr
, xpathCtx
)) == NULL
) {
353 // OSL_ENSURE(xpathCtx->lastError == NULL, xpathCtx->lastError->message);
354 xmlXPathFreeContext(xpathCtx
);
355 throw XPathException();
357 xmlXPathFreeContext(xpathCtx
);
358 Reference
< XXPathObject
> aObj(new CXPathObject(xpathObj
));
363 * same as eval but registers all namespace declarations found on namespaceNode
365 Reference
< XXPathObject
> SAL_CALL
CXPathAPI::evalNS(
366 const Reference
< XNode
>& contextNode
,
367 const OUString
& expr
,
368 const Reference
< XNode
>& namespaceNode
)
369 throw (RuntimeException
, XPathException
)
371 _collectNamespaces(this, namespaceNode
);
372 return eval(contextNode
, expr
);
376 * uses the service manager to create an instance of the service denoted by aName.
377 * If the returned object implements the XXPathExtension interface, it is added to the list
378 * of extensions that are used when evaluating XPath strings with this XPathAPI instance
380 void SAL_CALL
CXPathAPI::registerExtension(
381 const OUString
& aName
)
382 throw (RuntimeException
)
384 // get extension from service manager
385 Reference
< XXPathExtension
> aExtension(m_aFactory
->createInstance(aName
), UNO_QUERY_THROW
);
386 m_extensions
.push_back( aExtension
);
390 * registers the given extension instance to be used by XPath evaluations performed through this
393 void SAL_CALL
CXPathAPI::registerExtensionInstance(
394 const Reference
< XXPathExtension
>& aExtension
)
395 throw (RuntimeException
)
397 if (aExtension
.is()) {
398 m_extensions
.push_back( aExtension
);
400 throw RuntimeException();