1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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
;
63 #define _INPUT_BUFFER_SIZE 4096
64 #define _OUTPUT_BUFFER_SIZE 4096
68 const char* const LibXSLTTransformer::PARAM_SOURCE_URL
= "sourceURL";
69 const char* const LibXSLTTransformer::PARAM_SOURCE_BASE_URL
=
71 const char* const LibXSLTTransformer::PARAM_TARGET_URL
= "targetURL";
72 const char* const LibXSLTTransformer::PARAM_TARGET_BASE_URL
=
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
;
81 * ParserInputBufferCallback forwards IO call-backs to libxml stream IO.
83 struct ParserInputBufferCallback
86 on_read(void * context
, char * buffer
, int len
)
88 Reader
* tmp
= static_cast<Reader
*> (context
);
89 return tmp
->read(buffer
, len
);
92 on_close(void * context
)
94 Reader
* tmp
= static_cast<Reader
*> (context
);
95 return tmp
->closeInput();
99 * ParserOutputBufferCallback forwards IO call-backs to libxml stream IO.
101 struct ParserOutputBufferCallback
104 on_write(void * context
, const char * buffer
, int len
)
106 Reader
* tmp
= static_cast<Reader
*> (context
);
107 return tmp
->write(buffer
, len
);
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.
127 init(xsltTransformContextPtr
, const xmlChar
*)
132 insertByName(xmlXPathParserContextPtr ctxt
, int nargs
)
134 xsltTransformContextPtr tctxt
;
137 xsltGenericError(xsltGenericErrorContext
,
138 "insertByName: requires exactly 2 arguments\n");
141 tctxt
= xsltXPathGetTransformContext(ctxt
);
143 xsltGenericError(xsltGenericErrorContext
,
144 "xsltExtFunctionTest: failed to get the transformation context\n");
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
150 data
= tctxt
->_private
;
152 xsltGenericError(xsltGenericErrorContext
,
153 "xsltExtFunctionTest: failed to get module data\n");
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
);
177 static void getByName(xmlXPathParserContextPtr ctxt
, int nargs
)
179 xsltTransformContextPtr tctxt
;
182 xsltGenericError(xsltGenericErrorContext
,
183 "getByName: requires exactly 1 argument\n");
187 tctxt
= xsltXPathGetTransformContext(ctxt
);
189 xsltGenericError(xsltGenericErrorContext
,
190 "xsltExtFunctionTest: failed to get the transformation context\n");
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
;
197 xsltGenericError(xsltGenericErrorContext
,
198 "xsltExtFunctionTest: failed to get module data\n");
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
)
219 Reader::read(char * buffer
, int len
)
221 // const char *ptr = (const char *) context;
222 if (buffer
== NULL
|| len
< 0)
225 css::uno::Reference
<XInputStream
> xis
= this->m_transformer
->getInputStream();
226 n
= xis
.get()->readBytes(m_readBuf
, len
);
229 memcpy(buffer
, m_readBuf
.getArray(), n
);
235 Reader::write(const char * buffer
, int len
)
237 if (buffer
== NULL
|| 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
);
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
);
268 Reader::closeOutput()
270 css::uno::Reference
<XOutputStream
> xos
= m_transformer
->getOutputStream();
274 xos
.get()->closeOutput();
276 m_transformer
->done();
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
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
;
305 registerExtensionModule();
306 #if OSL_DEBUG_LEVEL > 1
307 xsltSetGenericDebugFunc(stderr
, NULL
);
308 xsltDebugDumpExtensions(NULL
);
310 OleHandler
* oh
= new OleHandler(m_transformer
->getServiceFactory());
313 tcontext
= xsltNewTransformContext(styleSheet
, doc
);
314 tcontext
->_private
= static_cast<void *> (oh
);
315 xsltQuoteUserParams(tcontext
, ¶ms
[0]);
316 result
= xsltApplyStylesheetUser(styleSheet
, doc
, 0, 0, 0,
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
);
332 xmlErrorPtr lastErr
= xmlGetLastError();
335 msg
= OUString::createFromAscii(lastErr
->message
);
337 msg
= OUString::createFromAscii(
338 "Unknown XSLT transformation error");
340 m_transformer
->error(msg
);
344 xsltFreeStylesheet(styleSheet
);
345 xsltFreeTransformContext(tcontext
);
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",
358 &ExtFuncOleCB::insertByName
);
359 xsltRegisterExtModuleFunction(
360 (const xmlChar
*) "getByName",
362 &ExtFuncOleCB::getByName
);
370 LibXSLTTransformer::LibXSLTTransformer(
371 const css::uno::Reference
<XMultiServiceFactory
> &r
) :
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
;
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
;
405 LibXSLTTransformer::addListener(const css::uno::Reference
<XStreamListener
>& listener
)
406 throw (RuntimeException
)
408 m_listeners
.insert(m_listeners
.begin(), listener
);
412 LibXSLTTransformer::removeListener(
413 const css::uno::Reference
<XStreamListener
>& listener
)
414 throw (RuntimeException
)
416 m_listeners
.remove(listener
);
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
;
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);
435 LibXSLTTransformer::error(const OUString
& msg
)
437 ListenerList
* l
= &m_listeners
;
439 arg
<<= Exception(msg
, *this);
440 for (ListenerList::iterator it
= l
->begin(); it
!= l
->end(); ++it
)
442 css::uno::Reference
<XStreamListener
> xl
= *it
;
445 xl
.get()->error(arg
);
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
;
465 LibXSLTTransformer::terminate() throw (RuntimeException
)
467 m_Reader
->terminate();
470 m_parameters
.clear();
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
482 xmlSubstituteEntitiesDefault(0);
483 m_parameters
.clear();
484 for (int i
= 0; i
< params
.getLength(); i
++)
488 OString nameUTF8
= OUStringToOString(nv
.Name
,
489 RTL_TEXTENCODING_UTF8
);
492 if (nv
.Value
>>= value
)
494 valueUTF8
= OUStringToOString(value
,
495 RTL_TEXTENCODING_UTF8
);
499 // ignore non-string parameters
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
));
535 LibXSLTTransformer::getStyleSheetURL()
537 return m_styleSheetURL
;
540 ::std::map
<const char*, OString
>
541 LibXSLTTransformer::getParameters()
546 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */