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