Line data Source code
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 <rtl/ustrbuf.hxx>
31 :
32 : #include <nodelist.hxx>
33 : #include <xpathobject.hxx>
34 :
35 : #include "../dom/node.hxx"
36 : #include "../dom/document.hxx"
37 :
38 : #include <cppuhelper/supportsservice.hxx>
39 :
40 : using namespace css::io;
41 : using namespace css::uno;
42 : using namespace css::xml::dom;
43 : using namespace css::xml::xpath;
44 : using css::lang::XMultiServiceFactory;
45 :
46 : namespace XPath
47 : {
48 : // factory
49 18836 : Reference< XInterface > CXPathAPI::_getInstance(const Reference< XMultiServiceFactory >& rSMgr)
50 : {
51 18836 : return Reference< XInterface >(static_cast<XXPathAPI*>(new CXPathAPI(rSMgr)));
52 : }
53 :
54 : // ctor
55 18836 : CXPathAPI::CXPathAPI(const Reference< XMultiServiceFactory >& rSMgr)
56 18836 : : m_aFactory(rSMgr)
57 : {
58 18836 : }
59 :
60 : const char* CXPathAPI::aImplementationName = "com.sun.star.comp.xml.xpath.XPathAPI";
61 : const char* CXPathAPI::aSupportedServiceNames[] = {
62 : "com.sun.star.xml.xpath.XPathAPI",
63 : NULL
64 : };
65 :
66 456 : OUString CXPathAPI::_getImplementationName()
67 : {
68 456 : return OUString::createFromAscii(aImplementationName);
69 : }
70 :
71 228 : Sequence<OUString> CXPathAPI::_getSupportedServiceNames()
72 : {
73 228 : Sequence<OUString> aSequence;
74 456 : for (int i=0; aSupportedServiceNames[i]!=NULL; i++) {
75 228 : aSequence.realloc(i+1);
76 228 : aSequence[i]=(OUString::createFromAscii(aSupportedServiceNames[i]));
77 : }
78 228 : return aSequence;
79 : }
80 :
81 0 : Sequence< OUString > SAL_CALL CXPathAPI::getSupportedServiceNames()
82 : throw (RuntimeException, std::exception)
83 : {
84 0 : return CXPathAPI::_getSupportedServiceNames();
85 : }
86 :
87 0 : OUString SAL_CALL CXPathAPI::getImplementationName()
88 : throw (RuntimeException, std::exception)
89 : {
90 0 : return CXPathAPI::_getImplementationName();
91 : }
92 :
93 0 : sal_Bool SAL_CALL CXPathAPI::supportsService(const OUString& aServiceName)
94 : throw (RuntimeException, std::exception)
95 : {
96 0 : return cppu::supportsService(this, aServiceName);
97 : }
98 :
99 72044 : void SAL_CALL CXPathAPI::registerNS(
100 : const OUString& aPrefix,
101 : const OUString& aURI)
102 : throw (RuntimeException, std::exception)
103 : {
104 72044 : ::osl::MutexGuard const g(m_Mutex);
105 :
106 72044 : m_nsmap.insert(nsmap_t::value_type(aPrefix, aURI));
107 72044 : }
108 :
109 2 : void SAL_CALL CXPathAPI::unregisterNS(
110 : const OUString& aPrefix,
111 : const OUString& aURI)
112 : throw (RuntimeException, std::exception)
113 : {
114 2 : ::osl::MutexGuard const g(m_Mutex);
115 :
116 2 : if ((m_nsmap.find(aPrefix))->second.equals(aURI)) {
117 2 : m_nsmap.erase(aPrefix);
118 2 : }
119 2 : }
120 :
121 : // register all namespaces stored in the namespace list for this object
122 : // with the current xpath evaluation context
123 339304 : static void lcl_registerNamespaces(
124 : xmlXPathContextPtr ctx,
125 : const nsmap_t& nsmap)
126 : {
127 339304 : nsmap_t::const_iterator i = nsmap.begin();
128 678608 : OString oprefix, ouri;
129 : xmlChar *p, *u;
130 2032278 : while (i != nsmap.end())
131 : {
132 1353670 : oprefix = OUStringToOString(i->first, RTL_TEXTENCODING_UTF8);
133 1353670 : ouri = OUStringToOString(i->second, RTL_TEXTENCODING_UTF8);
134 1353670 : p = (xmlChar*)oprefix.getStr();
135 1353670 : u = (xmlChar*)ouri.getStr();
136 1353670 : xmlXPathRegisterNs(ctx, p, u);
137 1353670 : ++i;
138 339304 : }
139 339304 : }
140 :
141 : // get all ns decls on a node (and parent nodes, if any)
142 18 : static void lcl_collectNamespaces(
143 : nsmap_t & rNamespaces, Reference< XNode > const& xNamespaceNode)
144 : {
145 18 : DOM::CNode *const pCNode(DOM::CNode::GetImplementation(xNamespaceNode));
146 18 : if (!pCNode) { throw RuntimeException(); }
147 :
148 18 : ::osl::MutexGuard const g(pCNode->GetOwnerDocument().GetMutex());
149 :
150 18 : xmlNodePtr pNode = pCNode->GetNodePtr();
151 78 : while (pNode != 0) {
152 42 : xmlNsPtr curDef = pNode->nsDef;
153 90 : while (curDef != 0) {
154 6 : const xmlChar* xHref = curDef->href;
155 6 : OUString aURI((sal_Char*)xHref, strlen((char*)xHref), RTL_TEXTENCODING_UTF8);
156 6 : const xmlChar* xPre = curDef->prefix;
157 12 : OUString aPrefix((sal_Char*)xPre, strlen((char*)xPre), RTL_TEXTENCODING_UTF8);
158 : // we could already have this prefix from a child node
159 6 : if (rNamespaces.find(aPrefix) == rNamespaces.end())
160 : {
161 6 : rNamespaces.insert(::std::make_pair(aPrefix, aURI));
162 : }
163 6 : curDef = curDef->next;
164 6 : }
165 42 : pNode = pNode->parent;
166 18 : }
167 18 : }
168 :
169 18 : static void lcl_collectRegisterNamespaces(
170 : CXPathAPI & rAPI, Reference< XNode > const& xNamespaceNode)
171 : {
172 18 : nsmap_t namespaces;
173 18 : lcl_collectNamespaces(namespaces, xNamespaceNode);
174 72 : for (nsmap_t::const_iterator iter = namespaces.begin();
175 48 : iter != namespaces.end(); ++iter)
176 : {
177 6 : rAPI.registerNS(iter->first, iter->second);
178 18 : }
179 18 : }
180 :
181 : // register function and variable lookup functions with the current
182 : // xpath evaluation context
183 339304 : static void lcl_registerExtensions(
184 : xmlXPathContextPtr ctx,
185 : const extensions_t& extensions)
186 : {
187 339304 : extensions_t::const_iterator i = extensions.begin();
188 678608 : while (i != extensions.end())
189 : {
190 0 : Libxml2ExtensionHandle aHandle = (*i)->getLibxml2ExtensionHandle();
191 0 : if ( aHandle.functionLookupFunction != 0 )
192 : {
193 : xmlXPathRegisterFuncLookup(ctx,
194 : reinterpret_cast<xmlXPathFuncLookupFunc>(
195 0 : sal::static_int_cast<sal_IntPtr>(aHandle.functionLookupFunction)),
196 : reinterpret_cast<void*>(
197 0 : sal::static_int_cast<sal_IntPtr>(aHandle.functionData)));
198 : }
199 0 : if ( aHandle.variableLookupFunction != 0 )
200 : {
201 : xmlXPathRegisterVariableLookup(ctx,
202 : reinterpret_cast<xmlXPathVariableLookupFunc>(
203 0 : sal::static_int_cast<sal_IntPtr>(aHandle.variableLookupFunction)),
204 : reinterpret_cast<void*>(
205 0 : sal::static_int_cast<sal_IntPtr>(aHandle.variableData)));
206 : }
207 0 : ++i;
208 : }
209 339304 : }
210 :
211 : /**
212 : * Use an XPath string to select a nodelist.
213 : */
214 354788 : Reference< XNodeList > SAL_CALL CXPathAPI::selectNodeList(
215 : const Reference< XNode >& contextNode,
216 : const OUString& expr)
217 : throw (RuntimeException, XPathException, std::exception)
218 : {
219 354788 : Reference< XXPathObject > xobj = eval(contextNode, expr);
220 339162 : return xobj->getNodeList();
221 : }
222 :
223 : /**
224 : * same as selectNodeList but registers all name space decalratiosn found on namespaceNode
225 : */
226 6 : Reference< XNodeList > SAL_CALL CXPathAPI::selectNodeListNS(
227 : const Reference< XNode >& contextNode,
228 : const OUString& expr,
229 : const Reference< XNode >& namespaceNode)
230 : throw (RuntimeException, XPathException, std::exception)
231 : {
232 6 : lcl_collectRegisterNamespaces(*this, namespaceNode);
233 6 : return selectNodeList(contextNode, expr);
234 : }
235 :
236 : /**
237 : * Same as selectNodeList but returns the first node (if any)
238 : */
239 318418 : Reference< XNode > SAL_CALL CXPathAPI::selectSingleNode(
240 : const Reference< XNode >& contextNode,
241 : const OUString& expr)
242 : throw (RuntimeException, XPathException, std::exception)
243 : {
244 318418 : Reference< XNodeList > aList = selectNodeList(contextNode, expr);
245 302800 : Reference< XNode > aNode = aList->item(0);
246 302800 : return aNode;
247 : }
248 :
249 : /**
250 : * Same as selectSingleNode but registers all namespaces declared on
251 : * namespaceNode
252 : */
253 6 : Reference< XNode > SAL_CALL CXPathAPI::selectSingleNodeNS(
254 : const Reference< XNode >& contextNode,
255 : const OUString& expr,
256 : const Reference< XNode >& namespaceNode )
257 : throw (RuntimeException, XPathException, std::exception)
258 : {
259 6 : lcl_collectRegisterNamespaces(*this, namespaceNode);
260 6 : return selectSingleNode(contextNode, expr);
261 : }
262 :
263 0 : static OUString make_error_message(xmlErrorPtr pError)
264 : {
265 0 : OUStringBuffer buf;
266 0 : if (pError) {
267 0 : if (pError->message) {
268 0 : buf.appendAscii(pError->message);
269 : }
270 0 : int line = pError->line;
271 0 : if (line) {
272 0 : buf.appendAscii("Line: ");
273 0 : buf.append(static_cast<sal_Int32>(line));
274 0 : buf.appendAscii("\n");
275 : }
276 0 : int column = pError->int2;
277 0 : if (column) {
278 0 : buf.appendAscii("Column: ");
279 0 : buf.append(static_cast<sal_Int32>(column));
280 0 : buf.appendAscii("\n");
281 : }
282 : } else {
283 0 : buf.appendAscii("no error argument!");
284 : }
285 0 : OUString msg = buf.makeStringAndClear();
286 0 : return msg;
287 : }
288 :
289 : extern "C" {
290 :
291 16 : static void generic_error_func(void *, const char *format, ...)
292 : {
293 : char str[1000];
294 : va_list args;
295 :
296 16 : va_start(args, format);
297 : #ifdef _WIN32
298 : #define vsnprintf _vsnprintf
299 : #endif
300 16 : vsnprintf(str, sizeof(str), format, args);
301 16 : va_end(args);
302 :
303 : SAL_WARN("unoxml", "libxml2 error: " << str);
304 16 : }
305 :
306 16 : static void structured_error_func(void *, xmlErrorPtr error)
307 : {
308 : SAL_WARN("unoxml", "libxml2 error: " << make_error_message(error));
309 16 : }
310 :
311 : } // extern "C"
312 :
313 : /**
314 : * evaluates an XPath string. relative XPath expressions are evaluated relative to
315 : * the context Node
316 : */
317 354922 : Reference< XXPathObject > SAL_CALL CXPathAPI::eval(
318 : Reference< XNode > const& xContextNode,
319 : const OUString& expr)
320 : throw (RuntimeException, XPathException, std::exception)
321 : {
322 354922 : if (!xContextNode.is()) { throw RuntimeException(); }
323 :
324 354910 : nsmap_t nsmap;
325 709820 : extensions_t extensions;
326 :
327 : {
328 354910 : ::osl::MutexGuard const g(m_Mutex);
329 354910 : nsmap = m_nsmap;
330 354910 : extensions = m_extensions;
331 : }
332 :
333 : // get the node and document
334 : ::rtl::Reference<DOM::CDocument> const pCDoc(
335 : dynamic_cast<DOM::CDocument*>( DOM::CNode::GetImplementation(
336 709820 : xContextNode->getOwnerDocument())));
337 354910 : if (!pCDoc.is()) { throw RuntimeException(); }
338 :
339 354910 : DOM::CNode *const pCNode = DOM::CNode::GetImplementation(xContextNode);
340 354910 : if (!pCNode) { throw RuntimeException(); }
341 :
342 709820 : ::osl::MutexGuard const g(pCDoc->GetMutex()); // lock the document!
343 :
344 354910 : xmlNodePtr const pNode = pCNode->GetNodePtr();
345 354910 : if (!pNode) { throw RuntimeException(); }
346 354910 : xmlDocPtr pDoc = pNode->doc;
347 :
348 : /* NB: workaround for #i87252#:
349 : libxml < 2.6.17 considers it an error if the context
350 : node is the empty document (i.e. its xpathCtx->doc has no
351 : children). libxml 2.6.17 does not consider it an error.
352 : Unfortunately, old libxml prints an error message to stderr,
353 : which (afaik) cannot be turned off in this case, so we handle it.
354 : */
355 354910 : if (!pDoc->children) {
356 15606 : throw XPathException();
357 : }
358 :
359 : /* Create xpath evaluation context */
360 : ::boost::shared_ptr<xmlXPathContext> const xpathCtx(
361 678608 : xmlXPathNewContext(pDoc), xmlXPathFreeContext);
362 339304 : if (xpathCtx == 0) { throw XPathException(); }
363 :
364 : // set context node
365 339304 : xpathCtx->node = pNode;
366 : // error handling
367 339304 : xpathCtx->error = structured_error_func;
368 339304 : xmlSetGenericErrorFunc(NULL, generic_error_func);
369 :
370 : // register namespaces and extension
371 339304 : lcl_registerNamespaces(xpathCtx.get(), nsmap);
372 339304 : lcl_registerExtensions(xpathCtx.get(), extensions);
373 :
374 : /* run the query */
375 678608 : OString o1 = OUStringToOString(expr, RTL_TEXTENCODING_UTF8);
376 339304 : xmlChar *xStr = (xmlChar*)o1.getStr();
377 : ::boost::shared_ptr<xmlXPathObject> const xpathObj(
378 678608 : xmlXPathEval(xStr, xpathCtx.get()), xmlXPathFreeObject);
379 339304 : xmlSetGenericErrorFunc(NULL, NULL);
380 339304 : if (0 == xpathObj) {
381 : // OSL_ENSURE(xpathCtx->lastError == NULL, xpathCtx->lastError->message);
382 16 : throw XPathException();
383 : }
384 : Reference<XXPathObject> const xObj(
385 339288 : new CXPathObject(pCDoc, pCDoc->GetMutex(), xpathObj));
386 694198 : return xObj;
387 : }
388 :
389 : /**
390 : * same as eval but registers all namespace declarations found on namespaceNode
391 : */
392 6 : Reference< XXPathObject > SAL_CALL CXPathAPI::evalNS(
393 : const Reference< XNode >& contextNode,
394 : const OUString& expr,
395 : const Reference< XNode >& namespaceNode)
396 : throw (RuntimeException, XPathException, std::exception)
397 : {
398 6 : lcl_collectRegisterNamespaces(*this, namespaceNode);
399 6 : return eval(contextNode, expr);
400 : }
401 :
402 : /**
403 : * uses the service manager to create an instance of the service denoted by aName.
404 : * If the returned object implements the XXPathExtension interface, it is added to the list
405 : * of extensions that are used when evaluating XPath strings with this XPathAPI instance
406 : */
407 0 : void SAL_CALL CXPathAPI::registerExtension(
408 : const OUString& aName)
409 : throw (RuntimeException, std::exception)
410 : {
411 0 : ::osl::MutexGuard const g(m_Mutex);
412 :
413 : // get extension from service manager
414 : Reference< XXPathExtension > const xExtension(
415 0 : m_aFactory->createInstance(aName), UNO_QUERY_THROW);
416 0 : m_extensions.push_back(xExtension);
417 0 : }
418 :
419 : /**
420 : * registers the given extension instance to be used by XPath evaluations performed through this
421 : * XPathAPI instance
422 : */
423 0 : void SAL_CALL CXPathAPI::registerExtensionInstance(
424 : Reference< XXPathExtension> const& xExtension)
425 : throw (RuntimeException, std::exception)
426 : {
427 0 : if (!xExtension.is()) {
428 0 : throw RuntimeException();
429 : }
430 0 : ::osl::MutexGuard const g(m_Mutex);
431 0 : m_extensions.push_back( xExtension );
432 0 : }
433 : }
434 :
435 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|