Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 :
3 : /*
4 : * Version: MPL 1.1 / GPLv3+ / LGPLv3+
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * [ Peter Jentsch <pjotr@guineapics.de> ]
18 : * Portions created by the Initial Developer are Copyright (C) 2010 the
19 : * Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s): Peter Jentsch <pjotr@guineapics.de>
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
25 : * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
26 : * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
27 : * instead of those above.
28 : */
29 :
30 :
31 : #include <cstdio>
32 : #include <cstring>
33 : #include <list>
34 : #include <map>
35 : #include <vector>
36 : #include <iostream>
37 : #include <libxml/parser.h>
38 : #include <libxml/tree.h>
39 : #include <libxml/xmlIO.h>
40 : #include <libxml/xpath.h>
41 : #include <libxml/xpathInternals.h>
42 : #include <libxml/xmlstring.h>
43 : #include <libxslt/transform.h>
44 : #include <libxslt/xsltutils.h>
45 : #include <libxslt/variables.h>
46 : #include <libxslt/extensions.h>
47 : #include <libexslt/exslt.h>
48 :
49 : #include <cppuhelper/factory.hxx>
50 : #include <cppuhelper/implbase4.hxx>
51 : #include <cppuhelper/implbase.hxx>
52 :
53 : #include <osl/module.h>
54 : #include <osl/file.hxx>
55 : #include <osl/process.h>
56 : #include <com/sun/star/lang/XComponent.hpp>
57 : #include <com/sun/star/lang/XInitialization.hpp>
58 : #include <com/sun/star/uno/Any.hxx>
59 : #include <com/sun/star/beans/NamedValue.hpp>
60 : #include <com/sun/star/io/XInputStream.hpp>
61 : #include <com/sun/star/io/XOutputStream.hpp>
62 : #include <com/sun/star/io/XActiveDataSource.hpp>
63 : #include <com/sun/star/io/XActiveDataSink.hpp>
64 : #include <com/sun/star/io/XActiveDataControl.hpp>
65 : #include <com/sun/star/io/XStreamListener.hpp>
66 :
67 : #include <LibXSLTTransformer.hxx>
68 : #include <OleHandler.hxx>
69 :
70 : using namespace ::rtl;
71 : using namespace ::cppu;
72 : using namespace ::osl;
73 : using namespace ::com::sun::star::beans;
74 : using namespace ::com::sun::star::io;
75 : using namespace ::com::sun::star::uno;
76 : using namespace ::com::sun::star::lang;
77 : using namespace ::com::sun::star::registry;
78 : using ::std::list;
79 : using ::std::map;
80 : using ::std::pair;
81 :
82 : #define _INPUT_BUFFER_SIZE 4096
83 : #define _OUTPUT_BUFFER_SIZE 4096
84 :
85 : namespace XSLT
86 : {
87 : const char* const LibXSLTTransformer::PARAM_SOURCE_URL = "sourceURL";
88 : const char* const LibXSLTTransformer::PARAM_SOURCE_BASE_URL =
89 : "sourceBaseURL";
90 : const char* const LibXSLTTransformer::PARAM_TARGET_URL = "targetURL";
91 : const char* const LibXSLTTransformer::PARAM_TARGET_BASE_URL =
92 : "targetBaseURL";
93 : const char* const LibXSLTTransformer::PARAM_DOCTYPE_PUBLIC = "publicType";
94 :
95 : const sal_Int32 Reader::OUTPUT_BUFFER_SIZE = _OUTPUT_BUFFER_SIZE;
96 :
97 : const sal_Int32 Reader::INPUT_BUFFER_SIZE = _INPUT_BUFFER_SIZE;
98 :
99 : /**
100 : * ParserInputBufferCallback forwards IO call-backs to libxml stream IO.
101 : */
102 : struct ParserInputBufferCallback
103 : {
104 : static int
105 0 : on_read(void * context, char * buffer, int len)
106 : {
107 0 : Reader * tmp = static_cast<Reader*> (context);
108 0 : return tmp->read(buffer, len);
109 : }
110 : static int
111 0 : on_close(void * context)
112 : {
113 0 : Reader * tmp = static_cast<Reader*> (context);
114 0 : return tmp->closeInput();
115 : }
116 : };
117 : /**
118 : * ParserOutputBufferCallback forwards IO call-backs to libxml stream IO.
119 : */
120 : struct ParserOutputBufferCallback
121 : {
122 : static int
123 0 : on_write(void * context, const char * buffer, int len)
124 : {
125 0 : Reader * tmp = static_cast<Reader*> (context);
126 0 : return tmp->write(buffer, len);
127 : }
128 : static int
129 0 : on_close(void * context)
130 : {
131 0 : Reader * tmp = static_cast<Reader*> (context);
132 0 : return tmp->closeOutput();
133 : }
134 : };
135 : /**
136 : * ExtFuncOleCB forwards XPath extension function calls registered with libxslt to the OleHandler instance that actually
137 : * provides the implementation for those functions.
138 : *
139 : * The OLE extension module currently supplies two functions
140 : * insertByName: registers an OLE object to be later inserted into the output tree.
141 : * getByName: reads a previously registered OLE object and returns a base64 encoded string representation.
142 : */
143 : struct ExtFuncOleCB
144 : {
145 : static void *
146 0 : init(xsltTransformContextPtr, const xmlChar*)
147 : {
148 0 : return NULL;
149 : }
150 : static void
151 0 : insertByName(xmlXPathParserContextPtr ctxt, int nargs)
152 : {
153 : xsltTransformContextPtr tctxt;
154 : void *data;
155 0 : if (nargs != 2) {
156 : xsltGenericError(xsltGenericErrorContext,
157 0 : "insertByName: requires exactly 2 arguments\n");
158 0 : return;
159 : }
160 0 : tctxt = xsltXPathGetTransformContext(ctxt);
161 0 : if (tctxt == NULL) {
162 : xsltGenericError(xsltGenericErrorContext,
163 0 : "xsltExtFunctionTest: failed to get the transformation context\n");
164 0 : return;
165 : }
166 : // XXX: someone with better knowledge of libxslt might come up with a better
167 : // idea to pass the OleHandler than by attaching it to tctxt->_private. See also
168 : // below.
169 0 : data = tctxt->_private;
170 0 : if (data == NULL) {
171 : xsltGenericError(xsltGenericErrorContext,
172 0 : "xsltExtFunctionTest: failed to get module data\n");
173 0 : return;
174 : }
175 0 : OleHandler * oh = static_cast<OleHandler*> (data);
176 :
177 0 : xmlXPathObjectPtr value = valuePop(ctxt);
178 0 : value = ensureStringValue(value, ctxt);
179 0 : xmlXPathObjectPtr streamName = valuePop(ctxt);
180 0 : streamName = ensureStringValue(streamName, ctxt);
181 :
182 0 : oh->insertByName(::rtl::OUString::createFromAscii((sal_Char*) streamName->stringval), ::rtl::OString((sal_Char*) value->stringval));
183 0 : valuePush(ctxt, xmlXPathNewCString(""));
184 : }
185 :
186 0 : static xmlXPathObjectPtr ensureStringValue(xmlXPathObjectPtr obj, const xmlXPathParserContextPtr ctxt)
187 : {
188 0 : if (obj->type != XPATH_STRING) {
189 0 : valuePush(ctxt, obj);
190 0 : xmlXPathStringFunction(ctxt, 1);
191 0 : obj = valuePop(ctxt);
192 : }
193 0 : return obj;
194 : }
195 :
196 0 : static void getByName(xmlXPathParserContextPtr ctxt, int nargs)
197 : {
198 : xsltTransformContextPtr tctxt;
199 : void *data;
200 0 : if (nargs != 1) {
201 : xsltGenericError(xsltGenericErrorContext,
202 0 : "getByName: requires exactly 1 argument\n");
203 : return;
204 : }
205 :
206 0 : tctxt = xsltXPathGetTransformContext(ctxt);
207 0 : if (tctxt == NULL) {
208 : xsltGenericError(xsltGenericErrorContext,
209 0 : "xsltExtFunctionTest: failed to get the transformation context\n");
210 : return;
211 : }
212 : // XXX: someone with better knowledge of libxslt might come up with a better
213 : // idea to pass the OleHandler than by attaching it to tctxt->_private
214 0 : data = tctxt->_private;
215 0 : if (data == NULL) {
216 : xsltGenericError(xsltGenericErrorContext,
217 0 : "xsltExtFunctionTest: failed to get module data\n");
218 : return;
219 : }
220 0 : OleHandler * oh = static_cast<OleHandler*> (data);
221 0 : xmlXPathObjectPtr streamName = valuePop(ctxt);
222 0 : streamName = ensureStringValue(streamName, ctxt);
223 0 : const OString content = oh->getByName(::rtl::OUString::createFromAscii((sal_Char*) streamName->stringval));
224 0 : valuePush(ctxt, xmlXPathNewCString(content.getStr()));
225 0 : xmlXPathFreeObject(streamName);
226 : }
227 : };
228 :
229 0 : Reader::Reader(LibXSLTTransformer* transformer) :
230 : Thread("LibXSLTTransformer"), m_transformer(transformer),
231 0 : m_readBuf(INPUT_BUFFER_SIZE), m_writeBuf(OUTPUT_BUFFER_SIZE)
232 : {
233 0 : LIBXML_TEST_VERSION;
234 0 : }
235 : ;
236 :
237 : int
238 0 : Reader::read(char * buffer, int len)
239 : {
240 : // const char *ptr = (const char *) context;
241 0 : if (buffer == NULL || len < 0)
242 0 : return (-1);
243 : sal_Int32 n;
244 0 : css::uno::Reference<XInputStream> xis = this->m_transformer->getInputStream();
245 0 : n = xis.get()->readBytes(m_readBuf, len);
246 0 : if (n > 0)
247 : {
248 0 : memcpy(buffer, m_readBuf.getArray(), n);
249 : }
250 0 : return n;
251 : }
252 :
253 : int
254 0 : Reader::write(const char * buffer, int len)
255 : {
256 0 : if (buffer == NULL || len < 0)
257 0 : return -1;
258 0 : if (len > 0)
259 : {
260 0 : css::uno::Reference<XOutputStream> xos = m_transformer->getOutputStream();
261 0 : sal_Int32 writeLen = len;
262 : sal_Int32 bufLen = ::std::min(writeLen,
263 0 : this->OUTPUT_BUFFER_SIZE);
264 : const sal_uInt8* memPtr =
265 0 : reinterpret_cast<const sal_uInt8*> (buffer);
266 0 : while (writeLen > 0)
267 : {
268 0 : sal_Int32 n = ::std::min(writeLen, bufLen);
269 0 : m_writeBuf.realloc(n);
270 0 : memcpy(m_writeBuf.getArray(), memPtr,
271 0 : static_cast<size_t> (n));
272 0 : xos.get()->writeBytes(m_writeBuf);
273 0 : memPtr += n;
274 0 : writeLen -= n;
275 0 : }
276 : }
277 0 : return len;
278 : }
279 :
280 : int
281 0 : Reader::closeInput()
282 : {
283 0 : return 0;
284 : }
285 :
286 : int
287 0 : Reader::closeOutput()
288 : {
289 0 : css::uno::Reference<XOutputStream> xos = m_transformer->getOutputStream();
290 0 : if (xos.is())
291 : {
292 0 : xos.get()->flush();
293 0 : xos.get()->closeOutput();
294 : }
295 0 : m_transformer->done();
296 0 : return 0;
297 : }
298 :
299 : void
300 0 : Reader::execute()
301 : {
302 : OSL_ASSERT(m_transformer != NULL);
303 : OSL_ASSERT(m_transformer->getInputStream().is());
304 : OSL_ASSERT(m_transformer->getOutputStream().is());
305 : OSL_ASSERT(!m_transformer->getStyleSheetURL().isEmpty());
306 0 : ::std::map<const char*, OString>::iterator pit;
307 0 : ::std::map<const char*, OString> pmap = m_transformer->getParameters();
308 0 : ::std::vector< const char* > params( pmap.size() * 2 + 1 ); // build parameters
309 0 : int paramIndex = 0;
310 0 : for (pit = pmap.begin(); pit != pmap.end(); ++pit)
311 : {
312 0 : params[paramIndex++] = (*pit).first;
313 0 : params[paramIndex++] = (*pit).second.getStr();
314 : }
315 0 : params[paramIndex] = NULL;
316 : xmlDocPtr doc = xmlReadIO(&ParserInputBufferCallback::on_read,
317 : &ParserInputBufferCallback::on_close,
318 0 : static_cast<void*> (this), NULL, NULL, 0);
319 : xsltStylesheetPtr styleSheet = xsltParseStylesheetFile(
320 0 : (const xmlChar *) m_transformer->getStyleSheetURL().getStr());
321 0 : xmlDocPtr result = NULL;
322 0 : xsltTransformContextPtr tcontext = NULL;
323 0 : exsltRegisterAll();
324 0 : registerExtensionModule();
325 : #if OSL_DEBUG_LEVEL > 1
326 : xsltSetGenericDebugFunc(stderr, NULL);
327 : xsltDebugDumpExtensions(NULL);
328 : #endif
329 0 : OleHandler* oh = new OleHandler(m_transformer->getServiceFactory());
330 0 : if (styleSheet)
331 : {
332 0 : tcontext = xsltNewTransformContext(styleSheet, doc);
333 0 : tcontext->_private = static_cast<void *> (oh);
334 0 : xsltQuoteUserParams(tcontext, ¶ms[0]);
335 : result = xsltApplyStylesheetUser(styleSheet, doc, 0, 0, 0,
336 0 : tcontext);
337 : }
338 :
339 0 : if (result)
340 : {
341 : xmlCharEncodingHandlerPtr encoder = xmlGetCharEncodingHandler(
342 0 : XML_CHAR_ENCODING_UTF8);
343 0 : xmlOutputBufferPtr outBuf = xmlAllocOutputBuffer(encoder);
344 0 : outBuf->context = static_cast<void *> (this);
345 0 : outBuf->writecallback = &ParserOutputBufferCallback::on_write;
346 0 : outBuf->closecallback = &ParserOutputBufferCallback::on_close;
347 0 : xsltSaveResultTo(outBuf, result, styleSheet);
348 : }
349 : else
350 : {
351 0 : xmlErrorPtr lastErr = xmlGetLastError();
352 0 : OUString msg;
353 0 : if (lastErr)
354 0 : msg = OUString::createFromAscii(lastErr->message);
355 : else
356 : msg = OUString::createFromAscii(
357 0 : "Unknown XSLT transformation error");
358 :
359 0 : m_transformer->error(msg);
360 : }
361 0 : closeOutput();
362 0 : delete(oh);
363 0 : xsltFreeStylesheet(styleSheet);
364 0 : xsltFreeTransformContext(tcontext);
365 0 : xmlFreeDoc(doc);
366 0 : xmlFreeDoc(result);
367 0 : }
368 :
369 : void
370 0 : Reader::registerExtensionModule()
371 : {
372 0 : const xmlChar* oleModuleURI = (const xmlChar *) EXT_MODULE_OLE_URI;
373 0 : xsltRegisterExtModule(oleModuleURI, &ExtFuncOleCB::init, NULL);
374 : xsltRegisterExtModuleFunction(
375 : (const xmlChar*) "insertByName",
376 : oleModuleURI,
377 0 : &ExtFuncOleCB::insertByName);
378 : xsltRegisterExtModuleFunction(
379 : (const xmlChar*) "getByName",
380 : oleModuleURI,
381 0 : &ExtFuncOleCB::getByName);
382 :
383 0 : }
384 :
385 0 : Reader::~Reader()
386 : {
387 0 : }
388 :
389 0 : LibXSLTTransformer::LibXSLTTransformer(
390 : const css::uno::Reference<XMultiServiceFactory> &r) :
391 0 : m_rServiceFactory(r)
392 : {
393 0 : }
394 :
395 : void
396 0 : LibXSLTTransformer::setInputStream(
397 : const css::uno::Reference<XInputStream>& inputStream)
398 : throw (RuntimeException)
399 : {
400 0 : m_rInputStream = inputStream;
401 0 : }
402 :
403 : css::uno::Reference<XInputStream>
404 0 : LibXSLTTransformer::getInputStream() throw (RuntimeException)
405 : {
406 0 : return m_rInputStream;
407 : }
408 :
409 : void
410 0 : LibXSLTTransformer::setOutputStream(
411 : const css::uno::Reference<XOutputStream>& outputStream)
412 : throw (RuntimeException)
413 : {
414 0 : m_rOutputStream = outputStream;
415 0 : }
416 :
417 : css::uno::Reference<XOutputStream>
418 0 : LibXSLTTransformer::getOutputStream() throw (RuntimeException)
419 : {
420 0 : return m_rOutputStream;
421 : }
422 :
423 : void
424 0 : LibXSLTTransformer::addListener(const css::uno::Reference<XStreamListener>& listener)
425 : throw (RuntimeException)
426 : {
427 0 : m_listeners.insert(m_listeners.begin(), listener);
428 0 : }
429 :
430 : void
431 0 : LibXSLTTransformer::removeListener(
432 : const css::uno::Reference<XStreamListener>& listener)
433 : throw (RuntimeException)
434 : {
435 0 : m_listeners.remove(listener);
436 0 : }
437 :
438 : void
439 0 : LibXSLTTransformer::start() throw (RuntimeException)
440 : {
441 0 : ListenerList::iterator it;
442 0 : ListenerList* l = &m_listeners;
443 0 : for (it = l->begin(); it != l->end(); ++it)
444 : {
445 0 : css::uno::Reference<XStreamListener> xl = *it;
446 0 : xl.get()->started();
447 0 : }
448 : OSL_ENSURE(!m_Reader.is(), "Somebody forgot to call terminate *and* holds a reference to this LibXSLTTransformer instance");
449 0 : m_Reader = new Reader(this);
450 0 : m_Reader->launch();
451 0 : }
452 :
453 : void
454 0 : LibXSLTTransformer::error(const OUString& msg)
455 : {
456 0 : ListenerList* l = &m_listeners;
457 0 : Any arg;
458 0 : arg <<= Exception(msg, *this);
459 0 : for (ListenerList::iterator it = l->begin(); it != l->end(); ++it)
460 : {
461 0 : css::uno::Reference<XStreamListener> xl = *it;
462 0 : if (xl.is())
463 : {
464 0 : xl.get()->error(arg);
465 : }
466 0 : }
467 0 : }
468 :
469 : void
470 0 : LibXSLTTransformer::done()
471 : {
472 0 : ListenerList* l = &m_listeners;
473 0 : for (ListenerList::iterator it = l->begin(); it != l->end(); ++it)
474 : {
475 0 : css::uno::Reference<XStreamListener> xl = *it;
476 0 : if (xl.is())
477 : {
478 0 : xl.get()->closed();
479 : }
480 0 : }
481 0 : }
482 :
483 : void
484 0 : LibXSLTTransformer::terminate() throw (RuntimeException)
485 : {
486 0 : m_Reader->terminate();
487 0 : m_Reader->join();
488 0 : m_Reader.clear();
489 0 : m_parameters.clear();
490 0 : }
491 :
492 : void
493 0 : LibXSLTTransformer::initialize(const Sequence<Any>& args)
494 : throw (RuntimeException)
495 : {
496 0 : Sequence<Any> params;
497 0 : args[0] >>= params;
498 0 : xmlSubstituteEntitiesDefault(0);
499 0 : m_parameters.clear();
500 0 : for (int i = 0; i < params.getLength(); i++)
501 : {
502 0 : NamedValue nv;
503 0 : params[i] >>= nv;
504 : OString nameUTF8 = OUStringToOString(nv.Name,
505 0 : RTL_TEXTENCODING_UTF8);
506 0 : OUString value;
507 0 : OString valueUTF8;
508 0 : if (nv.Value >>= value)
509 : {
510 : valueUTF8 = OUStringToOString(value,
511 0 : RTL_TEXTENCODING_UTF8);
512 : }
513 : else
514 : {
515 : // ignore non-string parameters
516 0 : continue;
517 : }
518 0 : if (nameUTF8.equals("StylesheetURL"))
519 : {
520 0 : m_styleSheetURL = valueUTF8;
521 : }
522 0 : else if (nameUTF8.equals("SourceURL"))
523 : {
524 : m_parameters.insert(pair<const char*, OString> (
525 0 : PARAM_SOURCE_URL, valueUTF8));
526 : }
527 0 : else if (nameUTF8.equals("SourceBaseURL"))
528 : {
529 : m_parameters.insert(pair<const char*, OString> (
530 0 : PARAM_SOURCE_BASE_URL, valueUTF8));
531 : }
532 0 : else if (nameUTF8.equals("TargetURL"))
533 : {
534 : m_parameters.insert(pair<const char*, OString> (
535 0 : PARAM_TARGET_URL, valueUTF8));
536 : }
537 0 : else if (nameUTF8.equals("TargetBaseURL"))
538 : {
539 : m_parameters.insert(pair<const char*, OString> (
540 0 : PARAM_TARGET_BASE_URL, valueUTF8));
541 : }
542 0 : else if (nameUTF8.equals("DoctypePublic"))
543 : {
544 : m_parameters.insert(pair<const char*, OString> (
545 0 : PARAM_DOCTYPE_PUBLIC, valueUTF8));
546 : }
547 0 : }
548 0 : }
549 :
550 : const OString
551 0 : LibXSLTTransformer::getStyleSheetURL()
552 : {
553 0 : return m_styleSheetURL;
554 : }
555 :
556 : ::std::map<const char*, OString>
557 0 : LibXSLTTransformer::getParameters()
558 : {
559 0 : return m_parameters;
560 : }
561 0 : }
562 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
563 :
|