LibreOffice Module unoxml (master) 1
xpathapi.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include "xpathapi.hxx"
21
22#include <stdarg.h>
23#include <string.h>
24
25#include <libxml/tree.h>
26#include <libxml/xmlerror.h>
27#include <libxml/xpath.h>
28#include <libxml/xpathInternals.h>
29
30#include <com/sun/star/xml/xpath/XPathException.hpp>
31
32#include <rtl/ustrbuf.hxx>
33#include <sal/log.hxx>
34
35#include "xpathobject.hxx"
36
37#include <node.hxx>
38#include "../dom/document.hxx"
39
42
43using namespace css::io;
44using namespace css::uno;
45using namespace css::xml::dom;
46using namespace css::xml::xpath;
47
48namespace XPath
49{
50 // ctor
51 CXPathAPI::CXPathAPI(const Reference< XComponentContext >& rxContext)
52 : m_xContext(rxContext)
53 {
54 }
55
56 Sequence< OUString > SAL_CALL CXPathAPI::getSupportedServiceNames()
57 {
58 return { "com.sun.star.xml.xpath.XPathAPI" };
59 }
60
62 {
63 return "com.sun.star.comp.xml.xpath.XPathAPI";
64 }
65
66 sal_Bool SAL_CALL CXPathAPI::supportsService(const OUString& aServiceName)
67 {
68 return cppu::supportsService(this, aServiceName);
69 }
70
71 void SAL_CALL CXPathAPI::registerNS(
72 const OUString& aPrefix,
73 const OUString& aURI)
74 {
75 std::scoped_lock const g(m_Mutex);
76
77 m_nsmap.emplace(aPrefix, aURI);
78 }
79
81 const OUString& aPrefix,
82 const OUString& aURI)
83 {
84 std::scoped_lock const g(m_Mutex);
85
86 if ((m_nsmap.find(aPrefix))->second == aURI) {
87 m_nsmap.erase(aPrefix);
88 }
89 }
90
91 // register all namespaces stored in the namespace list for this object
92 // with the current xpath evaluation context
94 xmlXPathContextPtr ctx,
95 const nsmap_t& nsmap)
96 {
97 OString oprefix, ouri;
98 for (const auto& rEntry : nsmap)
99 {
100 oprefix = OUStringToOString(rEntry.first, RTL_TEXTENCODING_UTF8);
101 ouri = OUStringToOString(rEntry.second, RTL_TEXTENCODING_UTF8);
102 xmlChar const *p = reinterpret_cast<xmlChar const *>(oprefix.getStr());
103 xmlChar const *u = reinterpret_cast<xmlChar const *>(ouri.getStr());
104 (void)xmlXPathRegisterNs(ctx, p, u);
105 }
106 }
107
108 // get all ns decls on a node (and parent nodes, if any)
110 nsmap_t & rNamespaces, Reference< XNode > const& xNamespaceNode)
111 {
112 DOM::CNode *const pCNode(dynamic_cast<DOM::CNode*>(xNamespaceNode.get()));
113 if (!pCNode) { throw RuntimeException(); }
114
115 ::osl::MutexGuard const g(pCNode->GetOwnerDocument().GetMutex());
116
117 xmlNodePtr pNode = pCNode->GetNodePtr();
118 while (pNode != nullptr) {
119 xmlNsPtr curDef = pNode->nsDef;
120 while (curDef != nullptr) {
121 const xmlChar* pHref = curDef->href;
122 OUString aURI(reinterpret_cast<char const *>(pHref), strlen(reinterpret_cast<char const *>(pHref)), RTL_TEXTENCODING_UTF8);
123 const xmlChar* pPre = curDef->prefix;
124 OUString aPrefix(reinterpret_cast<char const *>(pPre), strlen(reinterpret_cast<char const *>(pPre)), RTL_TEXTENCODING_UTF8);
125 // we could already have this prefix from a child node
126 rNamespaces.emplace(aPrefix, aURI);
127 curDef = curDef->next;
128 }
129 pNode = pNode->parent;
130 }
131 }
132
134 CXPathAPI & rAPI, Reference< XNode > const& xNamespaceNode)
135 {
137 lcl_collectNamespaces(namespaces, xNamespaceNode);
138 for (const auto& rEntry : namespaces)
139 {
140 rAPI.registerNS(rEntry.first, rEntry.second);
141 }
142 }
143
144 // register function and variable lookup functions with the current
145 // xpath evaluation context
147 xmlXPathContextPtr ctx,
149 {
150 for (const auto& rExtensionRef : extensions)
151 {
152 Libxml2ExtensionHandle aHandle = rExtensionRef->getLibxml2ExtensionHandle();
153 if ( aHandle.functionLookupFunction != 0 )
154 {
155 xmlXPathRegisterFuncLookup(ctx,
156 reinterpret_cast<xmlXPathFuncLookupFunc>(
157 sal::static_int_cast<sal_IntPtr>(aHandle.functionLookupFunction)),
158 reinterpret_cast<void*>(
159 sal::static_int_cast<sal_IntPtr>(aHandle.functionData)));
160 }
161 if ( aHandle.variableLookupFunction != 0 )
162 {
163 xmlXPathRegisterVariableLookup(ctx,
164 reinterpret_cast<xmlXPathVariableLookupFunc>(
165 sal::static_int_cast<sal_IntPtr>(aHandle.variableLookupFunction)),
166 reinterpret_cast<void*>(
167 sal::static_int_cast<sal_IntPtr>(aHandle.variableData)));
168 }
169 }
170 }
171
175 Reference< XNodeList > SAL_CALL CXPathAPI::selectNodeList(
176 const Reference< XNode >& contextNode,
177 const OUString& expr)
178 {
179 Reference< XXPathObject > xobj = eval(contextNode, expr);
180 return xobj->getNodeList();
181 }
182
186 Reference< XNodeList > SAL_CALL CXPathAPI::selectNodeListNS(
187 const Reference< XNode >& contextNode,
188 const OUString& expr,
189 const Reference< XNode >& namespaceNode)
190 {
191 lcl_collectRegisterNamespaces(*this, namespaceNode);
192 return selectNodeList(contextNode, expr);
193 }
194
198 Reference< XNode > SAL_CALL CXPathAPI::selectSingleNode(
199 const Reference< XNode >& contextNode,
200 const OUString& expr)
201 {
202 Reference< XNodeList > aList = selectNodeList(contextNode, expr);
203 Reference< XNode > aNode = aList->item(0);
204 return aNode;
205 }
206
211 Reference< XNode > SAL_CALL CXPathAPI::selectSingleNodeNS(
212 const Reference< XNode >& contextNode,
213 const OUString& expr,
214 const Reference< XNode >& namespaceNode )
215 {
216 lcl_collectRegisterNamespaces(*this, namespaceNode);
217 return selectSingleNode(contextNode, expr);
218 }
219
220 static OUString make_error_message(xmlErrorPtr pError)
221 {
222 OUStringBuffer buf;
223 if (pError) {
224 if (pError->message) {
225 buf.appendAscii(pError->message);
226 }
227 int line = pError->line;
228 if (line) {
229 buf.append("Line: " + OUString::number(static_cast<sal_Int32>(line)) + "\n");
230 }
231 int column = pError->int2;
232 if (column) {
233 buf.append("Column: " + OUString::number(static_cast<sal_Int32>(column)) + "\n");
234 }
235 } else {
236 buf.append("no error argument!");
237 }
238 OUString msg = buf.makeStringAndClear();
239 return msg;
240 }
241
242 extern "C" {
243
244#if defined __GNUC__
245 __attribute__ ((format (printf, 2, 3)))
246#endif
247 static void generic_error_func(void *, const char *format, ...)
248 {
249 char str[1000];
250 va_list args;
251
252 va_start(args, format);
253#ifdef _WIN32
254#define vsnprintf _vsnprintf
255#endif
256 vsnprintf(str, sizeof(str), format, args);
257 va_end(args);
258
259 SAL_WARN("unoxml", "libxml2 error: " << str);
260 }
261
262 static void structured_error_func(void *, xmlErrorPtr error)
263 {
264 SAL_WARN("unoxml", "libxml2 error: " << make_error_message(error));
265 }
266
267 } // extern "C"
268
273 Reference< XXPathObject > SAL_CALL CXPathAPI::eval(
274 Reference< XNode > const& xContextNode,
275 const OUString& expr)
276 {
277 if (!xContextNode.is()) { throw RuntimeException(); }
278
279 nsmap_t nsmap;
281
282 {
283 std::scoped_lock const g(m_Mutex);
284 nsmap = m_nsmap;
286 }
287
288 // get the node and document
290 dynamic_cast<DOM::CDocument*>(xContextNode->getOwnerDocument().get()));
291 if (!pCDoc.is()) { throw RuntimeException(); }
292
293 DOM::CNode *const pCNode = dynamic_cast<DOM::CNode*>(xContextNode.get());
294 if (!pCNode) { throw RuntimeException(); }
295
296 ::osl::MutexGuard const g(pCDoc->GetMutex()); // lock the document!
297
298 xmlNodePtr const pNode = pCNode->GetNodePtr();
299 if (!pNode) { throw RuntimeException(); }
300 xmlDocPtr pDoc = pNode->doc;
301
302 /* NB: workaround for #i87252#:
303 libxml < 2.6.17 considers it an error if the context
304 node is the empty document (i.e. its xpathCtx->doc has no
305 children). libxml 2.6.17 does not consider it an error.
306 Unfortunately, old libxml prints an error message to stderr,
307 which (afaik) cannot be turned off in this case, so we handle it.
308 */
309 if (!pDoc->children) {
310 throw XPathException();
311 }
312
313 /* Create xpath evaluation context */
314 std::shared_ptr<xmlXPathContext> const xpathCtx(
315 xmlXPathNewContext(pDoc), xmlXPathFreeContext);
316 if (xpathCtx == nullptr) { throw XPathException(); }
317
318 // set context node
319 xpathCtx->node = pNode;
320 // error handling
321 xpathCtx->error = structured_error_func;
322 xmlSetGenericErrorFunc(nullptr, generic_error_func);
323
324 // register namespaces and extension
325 lcl_registerNamespaces(xpathCtx.get(), nsmap);
326 lcl_registerExtensions(xpathCtx.get(), extensions);
327
328 /* run the query */
329 OString o1 = OUStringToOString(expr, RTL_TEXTENCODING_UTF8);
330 xmlChar const *pStr = reinterpret_cast<xmlChar const *>(o1.getStr());
331 std::shared_ptr<xmlXPathObject> const xpathObj(
332 xmlXPathEval(pStr, xpathCtx.get()), xmlXPathFreeObject);
333 xmlSetGenericErrorFunc(nullptr, nullptr);
334 if (nullptr == xpathObj) {
335 // OSL_ENSURE(xpathCtx->lastError == NULL, xpathCtx->lastError->message);
336 throw XPathException();
337 }
338 Reference<XXPathObject> const xObj(
339 new CXPathObject(pCDoc, pCDoc->GetMutex(), xpathObj));
340 return xObj;
341 }
342
346 Reference< XXPathObject > SAL_CALL CXPathAPI::evalNS(
347 const Reference< XNode >& contextNode,
348 const OUString& expr,
349 const Reference< XNode >& namespaceNode)
350 {
351 lcl_collectRegisterNamespaces(*this, namespaceNode);
352 return eval(contextNode, expr);
353 }
354
361 const OUString& aName)
362 {
363 std::scoped_lock const g(m_Mutex);
364
365 // get extension from service manager
366 Reference< XXPathExtension > const xExtension(
367 m_xContext->getServiceManager()->createInstanceWithContext(aName, m_xContext), UNO_QUERY_THROW);
368 m_extensions.push_back(xExtension);
369 }
370
376 Reference< XXPathExtension> const& xExtension)
377 {
378 if (!xExtension.is()) {
379 throw RuntimeException();
380 }
381 std::scoped_lock const g(m_Mutex);
382 m_extensions.push_back( xExtension );
383 }
384}
385
386extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
388 css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
389{
390 return cppu::acquire(new XPath::CXPathAPI(context));
391}
392
393/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XComponentContext > m_xContext
virtual void SAL_CALL registerNS(const OUString &aPrefix, const OUString &aURI) override
Definition: xpathapi.cxx:71
CXPathAPI(const css::uno::Reference< css::uno::XComponentContext > &)
Definition: xpathapi.cxx:51
virtual css::uno::Reference< css::xml::xpath::XXPathObject > SAL_CALL evalNS(const css::uno::Reference< css::xml::dom::XNode > &contextNode, const OUString &str, const css::uno::Reference< css::xml::dom::XNode > &namespaceNode) override
same as eval but registers all namespace declarations found on namespaceNode
Definition: xpathapi.cxx:346
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
Definition: xpathapi.cxx:66
virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL selectSingleNode(const css::uno::Reference< css::xml::dom::XNode > &contextNode, const OUString &str) override
Use an XPath string to select a single node.
Definition: xpathapi.cxx:198
virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL selectSingleNodeNS(const css::uno::Reference< css::xml::dom::XNode > &contextNode, const OUString &str, const css::uno::Reference< css::xml::dom::XNode > &namespaceNode) override
Use an XPath string to select a single node.
Definition: xpathapi.cxx:211
virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL selectNodeList(const css::uno::Reference< css::xml::dom::XNode > &contextNode, const OUString &str) override
Use an XPath string to select a nodelist.
Definition: xpathapi.cxx:175
virtual OUString SAL_CALL getImplementationName() override
Definition: xpathapi.cxx:61
virtual void SAL_CALL unregisterNS(const OUString &aPrefix, const OUString &aURI) override
Definition: xpathapi.cxx:80
const css::uno::Reference< css::uno::XComponentContext > m_xContext
Definition: xpathapi.hxx:58
virtual void SAL_CALL registerExtension(const OUString &aName) override
uses the service manager to create an instance of the service denoted by aName.
Definition: xpathapi.cxx:360
virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL selectNodeListNS(const css::uno::Reference< css::xml::dom::XNode > &contextNode, const OUString &str, const css::uno::Reference< css::xml::dom::XNode > &namespaceNode) override
Use an XPath string to select a nodelist.
Definition: xpathapi.cxx:186
nsmap_t m_nsmap
Definition: xpathapi.hxx:57
std::mutex m_Mutex
Definition: xpathapi.hxx:56
extensions_t m_extensions
Definition: xpathapi.hxx:59
virtual css::uno::Reference< css::xml::xpath::XXPathObject > SAL_CALL eval(const css::uno::Reference< css::xml::dom::XNode > &contextNode, const OUString &str) override
evaluates an XPath string.
Definition: xpathapi.cxx:273
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
Definition: xpathapi.cxx:56
virtual void SAL_CALL registerExtensionInstance(const css::uno::Reference< css::xml::xpath::XXPathExtension > &aExtension) override
registers the given extension instance to be used by XPath evaluations performed through this XPathAP...
Definition: xpathapi.cxx:375
float u
OUString aName
void * p
#define SAL_WARN(area, stream)
static void structured_error_func(void *, xmlErrorPtr error)
Definition: xpathapi.cxx:262
static void lcl_collectNamespaces(nsmap_t &rNamespaces, Reference< XNode > const &xNamespaceNode)
Definition: xpathapi.cxx:109
std::map< OUString, OUString > nsmap_t
Definition: xpathapi.hxx:43
static void lcl_registerExtensions(xmlXPathContextPtr ctx, const extensions_t &extensions)
Definition: xpathapi.cxx:146
std::vector< css::uno::Reference< css::xml::xpath::XXPathExtension > > extensions_t
Definition: xpathapi.hxx:44
static void generic_error_func(void *, const char *format,...)
Definition: xpathapi.cxx:247
static void lcl_collectRegisterNamespaces(CXPathAPI &rAPI, Reference< XNode > const &xNamespaceNode)
Definition: xpathapi.cxx:133
static void lcl_registerNamespaces(xmlXPathContextPtr ctx, const nsmap_t &nsmap)
Definition: xpathapi.cxx:93
static OUString make_error_message(xmlErrorPtr pError)
Definition: xpathapi.cxx:220
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
line
ctx
args
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
unsigned char sal_Bool
unsigned _Unwind_Word __attribute__((__mode__(__word__)))
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * unoxml_CXPathAPI_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
Definition: xpathapi.cxx:387