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 0 : Reference< XInterface > CXPathAPI::_getInstance(const Reference< XMultiServiceFactory >& rSMgr)
47 : {
48 0 : return Reference< XInterface >(static_cast<XXPathAPI*>(new CXPathAPI(rSMgr)));
49 : }
50 :
51 : // ctor
52 0 : CXPathAPI::CXPathAPI(const Reference< XMultiServiceFactory >& rSMgr)
53 0 : : m_aFactory(rSMgr)
54 : {
55 0 : }
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 0 : OUString CXPathAPI::_getImplementationName()
64 : {
65 0 : return OUString::createFromAscii(aImplementationName);
66 : }
67 :
68 0 : Sequence<OUString> CXPathAPI::_getSupportedServiceNames()
69 : {
70 0 : Sequence<OUString> aSequence;
71 0 : for (int i=0; aSupportedServiceNames[i]!=NULL; i++) {
72 0 : aSequence.realloc(i+1);
73 0 : aSequence[i]=(OUString::createFromAscii(aSupportedServiceNames[i]));
74 : }
75 0 : 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 0 : void SAL_CALL CXPathAPI::registerNS(
97 : const OUString& aPrefix,
98 : const OUString& aURI)
99 : throw (RuntimeException, std::exception)
100 : {
101 0 : ::osl::MutexGuard const g(m_Mutex);
102 :
103 0 : m_nsmap.insert(nsmap_t::value_type(aPrefix, aURI));
104 0 : }
105 :
106 0 : void SAL_CALL CXPathAPI::unregisterNS(
107 : const OUString& aPrefix,
108 : const OUString& aURI)
109 : throw (RuntimeException, std::exception)
110 : {
111 0 : ::osl::MutexGuard const g(m_Mutex);
112 :
113 0 : if ((m_nsmap.find(aPrefix))->second.equals(aURI)) {
114 0 : m_nsmap.erase(aPrefix);
115 0 : }
116 0 : }
117 :
118 : // register all namespaces stored in the namespace list for this object
119 : // with the current xpath evaluation context
120 0 : static void lcl_registerNamespaces(
121 : xmlXPathContextPtr ctx,
122 : const nsmap_t& nsmap)
123 : {
124 0 : nsmap_t::const_iterator i = nsmap.begin();
125 0 : OString oprefix, ouri;
126 : xmlChar *p, *u;
127 0 : while (i != nsmap.end())
128 : {
129 0 : oprefix = OUStringToOString(i->first, RTL_TEXTENCODING_UTF8);
130 0 : ouri = OUStringToOString(i->second, RTL_TEXTENCODING_UTF8);
131 0 : p = (xmlChar*)oprefix.getStr();
132 0 : u = (xmlChar*)ouri.getStr();
133 0 : xmlXPathRegisterNs(ctx, p, u);
134 0 : ++i;
135 0 : }
136 0 : }
137 :
138 : // get all ns decls on a node (and parent nodes, if any)
139 0 : static void lcl_collectNamespaces(
140 : nsmap_t & rNamespaces, Reference< XNode > const& xNamespaceNode)
141 : {
142 0 : DOM::CNode *const pCNode(DOM::CNode::GetImplementation(xNamespaceNode));
143 0 : if (!pCNode) { throw RuntimeException(); }
144 :
145 0 : ::osl::MutexGuard const g(pCNode->GetOwnerDocument().GetMutex());
146 :
147 0 : xmlNodePtr pNode = pCNode->GetNodePtr();
148 0 : while (pNode != 0) {
149 0 : xmlNsPtr curDef = pNode->nsDef;
150 0 : while (curDef != 0) {
151 0 : const xmlChar* xHref = curDef->href;
152 0 : OUString aURI((sal_Char*)xHref, strlen((char*)xHref), RTL_TEXTENCODING_UTF8);
153 0 : const xmlChar* xPre = curDef->prefix;
154 0 : OUString aPrefix((sal_Char*)xPre, strlen((char*)xPre), RTL_TEXTENCODING_UTF8);
155 : // we could already have this prefix from a child node
156 0 : if (rNamespaces.find(aPrefix) == rNamespaces.end())
157 : {
158 0 : rNamespaces.insert(::std::make_pair(aPrefix, aURI));
159 : }
160 0 : curDef = curDef->next;
161 0 : }
162 0 : pNode = pNode->parent;
163 0 : }
164 0 : }
165 :
166 0 : static void lcl_collectRegisterNamespaces(
167 : CXPathAPI & rAPI, Reference< XNode > const& xNamespaceNode)
168 : {
169 0 : nsmap_t namespaces;
170 0 : lcl_collectNamespaces(namespaces, xNamespaceNode);
171 0 : for (nsmap_t::const_iterator iter = namespaces.begin();
172 0 : iter != namespaces.end(); ++iter)
173 : {
174 0 : rAPI.registerNS(iter->first, iter->second);
175 0 : }
176 0 : }
177 :
178 : // register function and variable lookup functions with the current
179 : // xpath evaluation context
180 0 : static void lcl_registerExtensions(
181 : xmlXPathContextPtr ctx,
182 : const extensions_t& extensions)
183 : {
184 0 : extensions_t::const_iterator i = extensions.begin();
185 0 : 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 0 : }
207 :
208 : /**
209 : * Use an XPath string to select a nodelist.
210 : */
211 0 : Reference< XNodeList > SAL_CALL CXPathAPI::selectNodeList(
212 : const Reference< XNode >& contextNode,
213 : const OUString& expr)
214 : throw (RuntimeException, XPathException, std::exception)
215 : {
216 0 : Reference< XXPathObject > xobj = eval(contextNode, expr);
217 0 : return xobj->getNodeList();
218 : }
219 :
220 : /**
221 : * same as selectNodeList but registers all name space decalratiosn found on namespaceNode
222 : */
223 0 : 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 0 : lcl_collectRegisterNamespaces(*this, namespaceNode);
230 0 : return selectNodeList(contextNode, expr);
231 : }
232 :
233 : /**
234 : * Same as selectNodeList but returns the first node (if any)
235 : */
236 0 : Reference< XNode > SAL_CALL CXPathAPI::selectSingleNode(
237 : const Reference< XNode >& contextNode,
238 : const OUString& expr)
239 : throw (RuntimeException, XPathException, std::exception)
240 : {
241 0 : Reference< XNodeList > aList = selectNodeList(contextNode, expr);
242 0 : Reference< XNode > aNode = aList->item(0);
243 0 : return aNode;
244 : }
245 :
246 : /**
247 : * Same as selectSingleNode but registers all namespaces declared on
248 : * namespaceNode
249 : */
250 0 : 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 0 : lcl_collectRegisterNamespaces(*this, namespaceNode);
257 0 : 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 0 : static void generic_error_func(void *, const char *format, ...)
289 : {
290 : char str[1000];
291 : va_list args;
292 :
293 0 : va_start(args, format);
294 : #ifdef _WIN32
295 : #define vsnprintf _vsnprintf
296 : #endif
297 0 : vsnprintf(str, sizeof(str), format, args);
298 0 : va_end(args);
299 :
300 : SAL_WARN("unoxml", "libxml2 error: " << str);
301 0 : }
302 :
303 0 : static void structured_error_func(void *, xmlErrorPtr error)
304 : {
305 : SAL_WARN("unoxml", "libxml2 error: " << make_error_message(error));
306 0 : }
307 :
308 : } // extern "C"
309 :
310 : /**
311 : * evaluates an XPath string. relative XPath expressions are evaluated relative to
312 : * the context Node
313 : */
314 0 : Reference< XXPathObject > SAL_CALL CXPathAPI::eval(
315 : Reference< XNode > const& xContextNode,
316 : const OUString& expr)
317 : throw (RuntimeException, XPathException, std::exception)
318 : {
319 0 : if (!xContextNode.is()) { throw RuntimeException(); }
320 :
321 0 : nsmap_t nsmap;
322 0 : extensions_t extensions;
323 :
324 : {
325 0 : ::osl::MutexGuard const g(m_Mutex);
326 0 : nsmap = m_nsmap;
327 0 : 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 0 : xContextNode->getOwnerDocument())));
334 0 : if (!pCDoc.is()) { throw RuntimeException(); }
335 :
336 0 : DOM::CNode *const pCNode = DOM::CNode::GetImplementation(xContextNode);
337 0 : if (!pCNode) { throw RuntimeException(); }
338 :
339 0 : ::osl::MutexGuard const g(pCDoc->GetMutex()); // lock the document!
340 :
341 0 : xmlNodePtr const pNode = pCNode->GetNodePtr();
342 0 : if (!pNode) { throw RuntimeException(); }
343 0 : 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 0 : if (!pDoc->children) {
353 0 : throw XPathException();
354 : }
355 :
356 : /* Create xpath evaluation context */
357 : ::boost::shared_ptr<xmlXPathContext> const xpathCtx(
358 0 : xmlXPathNewContext(pDoc), xmlXPathFreeContext);
359 0 : if (xpathCtx == 0) { throw XPathException(); }
360 :
361 : // set context node
362 0 : xpathCtx->node = pNode;
363 : // error handling
364 0 : xpathCtx->error = structured_error_func;
365 0 : xmlSetGenericErrorFunc(NULL, generic_error_func);
366 :
367 : // register namespaces and extension
368 0 : lcl_registerNamespaces(xpathCtx.get(), nsmap);
369 0 : lcl_registerExtensions(xpathCtx.get(), extensions);
370 :
371 : /* run the query */
372 0 : OString o1 = OUStringToOString(expr, RTL_TEXTENCODING_UTF8);
373 0 : xmlChar *xStr = (xmlChar*)o1.getStr();
374 : ::boost::shared_ptr<xmlXPathObject> const xpathObj(
375 0 : xmlXPathEval(xStr, xpathCtx.get()), xmlXPathFreeObject);
376 0 : xmlSetGenericErrorFunc(NULL, NULL);
377 0 : if (0 == xpathObj) {
378 : // OSL_ENSURE(xpathCtx->lastError == NULL, xpathCtx->lastError->message);
379 0 : throw XPathException();
380 : }
381 : Reference<XXPathObject> const xObj(
382 0 : new CXPathObject(pCDoc, pCDoc->GetMutex(), xpathObj));
383 0 : return xObj;
384 : }
385 :
386 : /**
387 : * same as eval but registers all namespace declarations found on namespaceNode
388 : */
389 0 : 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 0 : lcl_collectRegisterNamespaces(*this, namespaceNode);
396 0 : 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: */
|