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