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