bump product version to 4.1.6.2
[LibreOffice.git] / filter / source / xsltfilter / LibXSLTTransformer.cxx
blobff747a52abc8d83a84fce353d46ab9757c0a095a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4 * This file is part of the LibreOffice project.
6 * This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 */
12 #include <cstdio>
13 #include <cstring>
14 #include <list>
15 #include <map>
16 #include <vector>
17 #include <iostream>
18 #include <libxml/parser.h>
19 #include <libxml/tree.h>
20 #include <libxml/xmlIO.h>
21 #include <libxml/xpath.h>
22 #include <libxml/xpathInternals.h>
23 #include <libxml/xmlstring.h>
24 #include <libxslt/transform.h>
25 #include <libxslt/xsltutils.h>
26 #include <libxslt/variables.h>
27 #include <libxslt/extensions.h>
28 #include <libexslt/exslt.h>
30 #include <cppuhelper/factory.hxx>
31 #include <cppuhelper/implbase4.hxx>
32 #include <cppuhelper/implbase.hxx>
34 #include <osl/module.h>
35 #include <osl/file.hxx>
36 #include <osl/process.h>
37 #include <com/sun/star/lang/XComponent.hpp>
38 #include <com/sun/star/lang/XInitialization.hpp>
39 #include <com/sun/star/uno/Any.hxx>
40 #include <com/sun/star/beans/NamedValue.hpp>
41 #include <com/sun/star/io/XInputStream.hpp>
42 #include <com/sun/star/io/XOutputStream.hpp>
43 #include <com/sun/star/io/XActiveDataSource.hpp>
44 #include <com/sun/star/io/XActiveDataSink.hpp>
45 #include <com/sun/star/io/XActiveDataControl.hpp>
46 #include <com/sun/star/io/XStreamListener.hpp>
48 #include <LibXSLTTransformer.hxx>
49 #include <OleHandler.hxx>
51 using namespace ::rtl;
52 using namespace ::cppu;
53 using namespace ::osl;
54 using namespace ::com::sun::star::beans;
55 using namespace ::com::sun::star::io;
56 using namespace ::com::sun::star::uno;
57 using namespace ::com::sun::star::lang;
58 using namespace ::com::sun::star::registry;
59 using ::std::list;
60 using ::std::map;
61 using ::std::pair;
63 #define _INPUT_BUFFER_SIZE 4096
64 #define _OUTPUT_BUFFER_SIZE 4096
66 namespace XSLT
68 const char* const LibXSLTTransformer::PARAM_SOURCE_URL = "sourceURL";
69 const char* const LibXSLTTransformer::PARAM_SOURCE_BASE_URL =
70 "sourceBaseURL";
71 const char* const LibXSLTTransformer::PARAM_TARGET_URL = "targetURL";
72 const char* const LibXSLTTransformer::PARAM_TARGET_BASE_URL =
73 "targetBaseURL";
74 const char* const LibXSLTTransformer::PARAM_DOCTYPE_PUBLIC = "publicType";
76 const sal_Int32 Reader::OUTPUT_BUFFER_SIZE = _OUTPUT_BUFFER_SIZE;
78 const sal_Int32 Reader::INPUT_BUFFER_SIZE = _INPUT_BUFFER_SIZE;
80 /**
81 * ParserInputBufferCallback forwards IO call-backs to libxml stream IO.
83 struct ParserInputBufferCallback
85 static int
86 on_read(void * context, char * buffer, int len)
88 Reader * tmp = static_cast<Reader*> (context);
89 return tmp->read(buffer, len);
91 static int
92 on_close(void * context)
94 Reader * tmp = static_cast<Reader*> (context);
95 return tmp->closeInput();
98 /**
99 * ParserOutputBufferCallback forwards IO call-backs to libxml stream IO.
101 struct ParserOutputBufferCallback
103 static int
104 on_write(void * context, const char * buffer, int len)
106 Reader * tmp = static_cast<Reader*> (context);
107 return tmp->write(buffer, len);
109 static int
110 on_close(void * context)
112 Reader * tmp = static_cast<Reader*> (context);
113 return tmp->closeOutput();
117 * ExtFuncOleCB forwards XPath extension function calls registered with libxslt to the OleHandler instance that actually
118 * provides the implementation for those functions.
120 * The OLE extension module currently supplies two functions
121 * insertByName: registers an OLE object to be later inserted into the output tree.
122 * getByName: reads a previously registered OLE object and returns a base64 encoded string representation.
124 struct ExtFuncOleCB
126 static void *
127 init(xsltTransformContextPtr, const xmlChar*)
129 return NULL;
131 static void
132 insertByName(xmlXPathParserContextPtr ctxt, int nargs)
134 xsltTransformContextPtr tctxt;
135 void *data;
136 if (nargs != 2) {
137 xsltGenericError(xsltGenericErrorContext,
138 "insertByName: requires exactly 2 arguments\n");
139 return;
141 tctxt = xsltXPathGetTransformContext(ctxt);
142 if (tctxt == NULL) {
143 xsltGenericError(xsltGenericErrorContext,
144 "xsltExtFunctionTest: failed to get the transformation context\n");
145 return;
147 // XXX: someone with better knowledge of libxslt might come up with a better
148 // idea to pass the OleHandler than by attaching it to tctxt->_private. See also
149 // below.
150 data = tctxt->_private;
151 if (data == NULL) {
152 xsltGenericError(xsltGenericErrorContext,
153 "xsltExtFunctionTest: failed to get module data\n");
154 return;
156 OleHandler * oh = static_cast<OleHandler*> (data);
158 xmlXPathObjectPtr value = valuePop(ctxt);
159 value = ensureStringValue(value, ctxt);
160 xmlXPathObjectPtr streamName = valuePop(ctxt);
161 streamName = ensureStringValue(streamName, ctxt);
163 oh->insertByName(OUString::createFromAscii((sal_Char*) streamName->stringval), OString((sal_Char*) value->stringval));
164 valuePush(ctxt, xmlXPathNewCString(""));
167 static xmlXPathObjectPtr ensureStringValue(xmlXPathObjectPtr obj, const xmlXPathParserContextPtr ctxt)
169 if (obj->type != XPATH_STRING) {
170 valuePush(ctxt, obj);
171 xmlXPathStringFunction(ctxt, 1);
172 obj = valuePop(ctxt);
174 return obj;
177 static void getByName(xmlXPathParserContextPtr ctxt, int nargs)
179 xsltTransformContextPtr tctxt;
180 void *data;
181 if (nargs != 1) {
182 xsltGenericError(xsltGenericErrorContext,
183 "getByName: requires exactly 1 argument\n");
184 return;
187 tctxt = xsltXPathGetTransformContext(ctxt);
188 if (tctxt == NULL) {
189 xsltGenericError(xsltGenericErrorContext,
190 "xsltExtFunctionTest: failed to get the transformation context\n");
191 return;
193 // XXX: someone with better knowledge of libxslt might come up with a better
194 // idea to pass the OleHandler than by attaching it to tctxt->_private
195 data = tctxt->_private;
196 if (data == NULL) {
197 xsltGenericError(xsltGenericErrorContext,
198 "xsltExtFunctionTest: failed to get module data\n");
199 return;
201 OleHandler * oh = static_cast<OleHandler*> (data);
202 xmlXPathObjectPtr streamName = valuePop(ctxt);
203 streamName = ensureStringValue(streamName, ctxt);
204 const OString content = oh->getByName(OUString::createFromAscii((sal_Char*) streamName->stringval));
205 valuePush(ctxt, xmlXPathNewCString(content.getStr()));
206 xmlXPathFreeObject(streamName);
210 Reader::Reader(LibXSLTTransformer* transformer) :
211 Thread("LibXSLTTransformer"), m_transformer(transformer),
212 m_readBuf(INPUT_BUFFER_SIZE), m_writeBuf(OUTPUT_BUFFER_SIZE)
214 LIBXML_TEST_VERSION;
219 Reader::read(char * buffer, int len)
221 // const char *ptr = (const char *) context;
222 if (buffer == NULL || len < 0)
223 return (-1);
224 sal_Int32 n;
225 css::uno::Reference<XInputStream> xis = this->m_transformer->getInputStream();
226 n = xis.get()->readBytes(m_readBuf, len);
227 if (n > 0)
229 memcpy(buffer, m_readBuf.getArray(), n);
231 return n;
235 Reader::write(const char * buffer, int len)
237 if (buffer == NULL || len < 0)
238 return -1;
239 if (len > 0)
241 css::uno::Reference<XOutputStream> xos = m_transformer->getOutputStream();
242 sal_Int32 writeLen = len;
243 sal_Int32 bufLen = ::std::min(writeLen,
244 this->OUTPUT_BUFFER_SIZE);
245 const sal_uInt8* memPtr =
246 reinterpret_cast<const sal_uInt8*> (buffer);
247 while (writeLen > 0)
249 sal_Int32 n = ::std::min(writeLen, bufLen);
250 m_writeBuf.realloc(n);
251 memcpy(m_writeBuf.getArray(), memPtr,
252 static_cast<size_t> (n));
253 xos.get()->writeBytes(m_writeBuf);
254 memPtr += n;
255 writeLen -= n;
258 return len;
262 Reader::closeInput()
264 return 0;
268 Reader::closeOutput()
270 css::uno::Reference<XOutputStream> xos = m_transformer->getOutputStream();
271 if (xos.is())
273 xos.get()->flush();
274 xos.get()->closeOutput();
276 m_transformer->done();
277 return 0;
280 void
281 Reader::execute()
283 OSL_ASSERT(m_transformer != NULL);
284 OSL_ASSERT(m_transformer->getInputStream().is());
285 OSL_ASSERT(m_transformer->getOutputStream().is());
286 OSL_ASSERT(!m_transformer->getStyleSheetURL().isEmpty());
287 ::std::map<const char*, OString>::iterator pit;
288 ::std::map<const char*, OString> pmap = m_transformer->getParameters();
289 ::std::vector< const char* > params( pmap.size() * 2 + 1 ); // build parameters
290 int paramIndex = 0;
291 for (pit = pmap.begin(); pit != pmap.end(); ++pit)
293 params[paramIndex++] = (*pit).first;
294 params[paramIndex++] = (*pit).second.getStr();
296 params[paramIndex] = NULL;
297 xmlDocPtr doc = xmlReadIO(&ParserInputBufferCallback::on_read,
298 &ParserInputBufferCallback::on_close,
299 static_cast<void*> (this), NULL, NULL, 0);
300 xsltStylesheetPtr styleSheet = xsltParseStylesheetFile(
301 (const xmlChar *) m_transformer->getStyleSheetURL().getStr());
302 xmlDocPtr result = NULL;
303 xsltTransformContextPtr tcontext = NULL;
304 exsltRegisterAll();
305 registerExtensionModule();
306 #if OSL_DEBUG_LEVEL > 1
307 xsltSetGenericDebugFunc(stderr, NULL);
308 xsltDebugDumpExtensions(NULL);
309 #endif
310 OleHandler* oh = new OleHandler(m_transformer->getServiceFactory());
311 if (styleSheet)
313 tcontext = xsltNewTransformContext(styleSheet, doc);
314 tcontext->_private = static_cast<void *> (oh);
315 xsltQuoteUserParams(tcontext, &params[0]);
316 result = xsltApplyStylesheetUser(styleSheet, doc, 0, 0, 0,
317 tcontext);
320 if (result)
322 xmlCharEncodingHandlerPtr encoder = xmlGetCharEncodingHandler(
323 XML_CHAR_ENCODING_UTF8);
324 xmlOutputBufferPtr outBuf = xmlAllocOutputBuffer(encoder);
325 outBuf->context = static_cast<void *> (this);
326 outBuf->writecallback = &ParserOutputBufferCallback::on_write;
327 outBuf->closecallback = &ParserOutputBufferCallback::on_close;
328 xsltSaveResultTo(outBuf, result, styleSheet);
330 else
332 xmlErrorPtr lastErr = xmlGetLastError();
333 OUString msg;
334 if (lastErr)
335 msg = OUString::createFromAscii(lastErr->message);
336 else
337 msg = OUString::createFromAscii(
338 "Unknown XSLT transformation error");
340 m_transformer->error(msg);
342 closeOutput();
343 delete(oh);
344 xsltFreeStylesheet(styleSheet);
345 xsltFreeTransformContext(tcontext);
346 xmlFreeDoc(doc);
347 xmlFreeDoc(result);
350 void
351 Reader::registerExtensionModule()
353 const xmlChar* oleModuleURI = (const xmlChar *) EXT_MODULE_OLE_URI;
354 xsltRegisterExtModule(oleModuleURI, &ExtFuncOleCB::init, NULL);
355 xsltRegisterExtModuleFunction(
356 (const xmlChar*) "insertByName",
357 oleModuleURI,
358 &ExtFuncOleCB::insertByName);
359 xsltRegisterExtModuleFunction(
360 (const xmlChar*) "getByName",
361 oleModuleURI,
362 &ExtFuncOleCB::getByName);
366 Reader::~Reader()
370 LibXSLTTransformer::LibXSLTTransformer(
371 const css::uno::Reference<XMultiServiceFactory> &r) :
372 m_rServiceFactory(r)
376 void
377 LibXSLTTransformer::setInputStream(
378 const css::uno::Reference<XInputStream>& inputStream)
379 throw (RuntimeException)
381 m_rInputStream = inputStream;
384 css::uno::Reference<XInputStream>
385 LibXSLTTransformer::getInputStream() throw (RuntimeException)
387 return m_rInputStream;
390 void
391 LibXSLTTransformer::setOutputStream(
392 const css::uno::Reference<XOutputStream>& outputStream)
393 throw (RuntimeException)
395 m_rOutputStream = outputStream;
398 css::uno::Reference<XOutputStream>
399 LibXSLTTransformer::getOutputStream() throw (RuntimeException)
401 return m_rOutputStream;
404 void
405 LibXSLTTransformer::addListener(const css::uno::Reference<XStreamListener>& listener)
406 throw (RuntimeException)
408 m_listeners.insert(m_listeners.begin(), listener);
411 void
412 LibXSLTTransformer::removeListener(
413 const css::uno::Reference<XStreamListener>& listener)
414 throw (RuntimeException)
416 m_listeners.remove(listener);
419 void
420 LibXSLTTransformer::start() throw (RuntimeException)
422 ListenerList::iterator it;
423 ListenerList* l = &m_listeners;
424 for (it = l->begin(); it != l->end(); ++it)
426 css::uno::Reference<XStreamListener> xl = *it;
427 xl.get()->started();
429 OSL_ENSURE(!m_Reader.is(), "Somebody forgot to call terminate *and* holds a reference to this LibXSLTTransformer instance");
430 m_Reader = new Reader(this);
431 m_Reader->launch();
434 void
435 LibXSLTTransformer::error(const OUString& msg)
437 ListenerList* l = &m_listeners;
438 Any arg;
439 arg <<= Exception(msg, *this);
440 for (ListenerList::iterator it = l->begin(); it != l->end(); ++it)
442 css::uno::Reference<XStreamListener> xl = *it;
443 if (xl.is())
445 xl.get()->error(arg);
450 void
451 LibXSLTTransformer::done()
453 ListenerList* l = &m_listeners;
454 for (ListenerList::iterator it = l->begin(); it != l->end(); ++it)
456 css::uno::Reference<XStreamListener> xl = *it;
457 if (xl.is())
459 xl.get()->closed();
464 void
465 LibXSLTTransformer::terminate() throw (RuntimeException)
467 m_Reader->terminate();
468 m_Reader->join();
469 m_Reader.clear();
470 m_parameters.clear();
473 void
474 LibXSLTTransformer::initialize(const Sequence<Any>& args)
475 throw (RuntimeException)
477 Sequence<Any> params;
478 if (!(args[0] >>= params))
479 { // backward compatibility for old clients using createInstance
480 params = args;
482 xmlSubstituteEntitiesDefault(0);
483 m_parameters.clear();
484 for (int i = 0; i < params.getLength(); i++)
486 NamedValue nv;
487 params[i] >>= nv;
488 OString nameUTF8 = OUStringToOString(nv.Name,
489 RTL_TEXTENCODING_UTF8);
490 OUString value;
491 OString valueUTF8;
492 if (nv.Value >>= value)
494 valueUTF8 = OUStringToOString(value,
495 RTL_TEXTENCODING_UTF8);
497 else
499 // ignore non-string parameters
500 continue;
502 if (nameUTF8.equals("StylesheetURL"))
504 m_styleSheetURL = valueUTF8;
506 else if (nameUTF8.equals("SourceURL"))
508 m_parameters.insert(pair<const char*, OString> (
509 PARAM_SOURCE_URL, valueUTF8));
511 else if (nameUTF8.equals("SourceBaseURL"))
513 m_parameters.insert(pair<const char*, OString> (
514 PARAM_SOURCE_BASE_URL, valueUTF8));
516 else if (nameUTF8.equals("TargetURL"))
518 m_parameters.insert(pair<const char*, OString> (
519 PARAM_TARGET_URL, valueUTF8));
521 else if (nameUTF8.equals("TargetBaseURL"))
523 m_parameters.insert(pair<const char*, OString> (
524 PARAM_TARGET_BASE_URL, valueUTF8));
526 else if (nameUTF8.equals("DoctypePublic"))
528 m_parameters.insert(pair<const char*, OString> (
529 PARAM_DOCTYPE_PUBLIC, valueUTF8));
534 const OString
535 LibXSLTTransformer::getStyleSheetURL()
537 return m_styleSheetURL;
540 ::std::map<const char*, OString>
541 LibXSLTTransformer::getParameters()
543 return m_parameters;
546 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */