bump product version to 7.6.3.2-android
[LibreOffice.git] / filter / source / xsltfilter / XSLTFilter.cxx
blob771b5a37f8fc82419aec1c5e15f2a8f58c2b47cb
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <cppuhelper/factory.hxx>
22 #include <cppuhelper/implbase.hxx>
23 #include <cppuhelper/supportsservice.hxx>
25 #include <sax/tools/documenthandleradapter.hxx>
27 #include <osl/diagnose.h>
28 #include <osl/time.h>
29 #include <osl/conditn.hxx>
30 #include <tools/urlobj.hxx>
31 #include <comphelper/diagnose_ex.hxx>
32 #include <sal/log.hxx>
33 #include <rtl/ref.hxx>
34 #include <rtl/uri.hxx>
36 #include <comphelper/interaction.hxx>
38 #include <com/sun/star/lang/EventObject.hpp>
39 #include <com/sun/star/lang/XServiceInfo.hpp>
41 #include <com/sun/star/uno/Any.hxx>
43 #include <com/sun/star/beans/PropertyValue.hpp>
45 #include <com/sun/star/xml/sax/Parser.hpp>
46 #include <com/sun/star/xml/sax/InputSource.hpp>
47 #include <com/sun/star/xml/sax/XDocumentHandler.hpp>
48 #include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
49 #include <com/sun/star/xml/sax/XFastParser.hpp>
50 #include <com/sun/star/xml/sax/Writer.hpp>
51 #include <com/sun/star/xml/XImportFilter.hpp>
52 #include <com/sun/star/xml/XImportFilter2.hpp>
53 #include <com/sun/star/xml/XExportFilter.hpp>
55 #include <com/sun/star/util/theMacroExpander.hpp>
57 #include <com/sun/star/io/Pipe.hpp>
58 #include <com/sun/star/io/XInputStream.hpp>
59 #include <com/sun/star/io/XOutputStream.hpp>
60 #include <com/sun/star/io/XActiveDataSource.hpp>
61 #include <com/sun/star/io/XSeekable.hpp>
62 #include <com/sun/star/io/XStreamListener.hpp>
63 #include <com/sun/star/util/PathSubstitution.hpp>
64 #include <com/sun/star/util/XStringSubstitution.hpp>
65 #include <com/sun/star/beans/NamedValue.hpp>
66 #include <com/sun/star/task/XInteractionHandler.hpp>
67 #include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
68 #include <com/sun/star/xml/xslt/XSLTTransformer.hpp>
69 #include <utility>
71 #define TRANSFORMATION_TIMEOUT_SEC 60
73 using namespace ::cppu;
74 using namespace ::osl;
75 using namespace ::sax;
76 using namespace ::com::sun::star::beans;
77 using namespace ::com::sun::star::io;
78 using namespace ::com::sun::star::uno;
79 using namespace ::com::sun::star::lang;
80 using namespace ::com::sun::star::registry;
81 using namespace ::com::sun::star::xml;
82 using namespace ::com::sun::star::xml::sax;
83 using namespace ::com::sun::star::util;
84 using namespace ::com::sun::star::task;
86 namespace XSLT
88 namespace {
90 class XSLTFilter;
91 class XSLTFilterStreamListener : public WeakImplHelper<XStreamListener>
93 public:
94 XSLTFilterStreamListener(XSLTFilter& rParent) : m_rParent(rParent) {}
96 // XStreamListener
97 virtual void SAL_CALL
98 error(const Any& a) override;
99 virtual void SAL_CALL
100 closed() override;
101 virtual void SAL_CALL
102 terminated() override;
103 virtual void SAL_CALL
104 started() override;
105 virtual void SAL_CALL
106 disposing(const EventObject& e) override;
107 private:
108 XSLTFilter& m_rParent;
112 * XSLTFilter reads flat XML streams from the XML filter framework and passes
113 * them to an XSLT transformation service. XSLT transformation errors are
114 * reported to XSLTFilter.
116 * Currently, our transformation service is libxslt based, so it
117 * only supports XSLT 1.0. There is a possibility to use XSLT 2.0
118 * supporting service from an extension for a specific filter; the
119 * service must support com.sun.star.xml.xslt.XSLT2Transformer.
121 class XSLTFilter : public WeakImplHelper<XImportFilter, XImportFilter2, XExportFilter,
122 ExtendedDocumentHandlerAdapter, XServiceInfo>
124 friend class XSLTFilterStreamListener;
125 private:
127 // the UNO ServiceFactory
128 css::uno::Reference<XComponentContext> m_xContext;
130 // DocumentHandler interface of the css::xml::sax::Writer service
131 css::uno::Reference<XOutputStream> m_rOutputStream;
133 css::uno::Reference<xslt::XXSLTTransformer> m_tcontrol;
135 osl::Condition m_cTransformed;
136 bool m_bTerminated;
137 bool m_bError;
139 OUString m_aExportBaseUrl;
141 OUString
142 rel2abs(const OUString&);
143 OUString
144 expandUrl(const OUString&);
146 css::uno::Reference<xslt::XXSLTTransformer> impl_createTransformer(const OUString& rTransformer, const Sequence<Any>& rArgs);
148 public:
150 // ctor...
151 explicit XSLTFilter(css::uno::Reference<XComponentContext> x);
153 // XServiceInfo
154 virtual sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override;
155 virtual OUString SAL_CALL getImplementationName() override;
156 virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
158 // XImportFilter
159 virtual sal_Bool SAL_CALL
160 importer(const Sequence<PropertyValue>& aSourceData, const css::uno::Reference<
161 XDocumentHandler>& xHandler,
162 const Sequence<OUString>& msUserData) override;
164 // XImportFilter2
165 virtual sal_Bool SAL_CALL
166 importer(const Sequence<PropertyValue>& aSourceData, const css::uno::Reference<
167 XFastParser>& xFastParser,
168 const Sequence<OUString>& msUserData) override;
170 // XExportFilter
171 virtual sal_Bool SAL_CALL
172 exporter(const Sequence<PropertyValue>& aSourceData, const Sequence<
173 OUString>& msUserData) override;
175 // XDocumentHandler
176 virtual void SAL_CALL
177 startDocument() override;
178 virtual void SAL_CALL
179 endDocument() override;
184 XSLTFilter::XSLTFilter(css::uno::Reference<XComponentContext> x):
185 m_xContext(std::move(x)), m_bTerminated(false), m_bError(false)
188 void
189 XSLTFilterStreamListener::disposing(const EventObject&)
193 // XServiceInfo
194 sal_Bool XSLTFilter::supportsService(const OUString& sServiceName)
196 return cppu::supportsService(this, sServiceName);
198 OUString XSLTFilter::getImplementationName()
200 return "com.sun.star.comp.documentconversion.XSLTFilter";
202 css::uno::Sequence< OUString > XSLTFilter::getSupportedServiceNames()
204 return { "com.sun.star.documentconversion.XSLTFilter" };
207 OUString
208 XSLTFilter::expandUrl(const OUString& sUrl)
210 OUString sPreparedURL(sUrl);
211 if (sPreparedURL.startsWithIgnoreAsciiCase("vnd.sun.star.expand:", &sPreparedURL))
213 sPreparedURL = rtl::Uri::decode(sPreparedURL, rtl_UriDecodeWithCharset,
214 RTL_TEXTENCODING_UTF8);
215 css::uno::Reference<XMacroExpander>
216 xMacroExpander = theMacroExpander::get(m_xContext);
217 sPreparedURL = xMacroExpander->expandMacros(sPreparedURL);
219 return sPreparedURL;
222 css::uno::Reference<xslt::XXSLTTransformer>
223 XSLTFilter::impl_createTransformer(const OUString& rTransformer, const Sequence<Any>& rArgs)
225 css::uno::Reference<xslt::XXSLTTransformer> xTransformer;
227 // check if the filter needs XSLT-2.0-capable transformer
228 // COMPATIBILITY: libreoffice 3.5/3.6 used to save the impl.
229 // name of the XSLT 2.0 transformation service there, so check
230 // for that too (it is sufficient to check that there is _a_
231 // service name there)
232 if (rTransformer.toBoolean() || rTransformer.startsWith("com.sun."))
236 xTransformer.set(
237 m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
238 "com.sun.star.xml.xslt.XSLT2Transformer", rArgs, m_xContext),
239 css::uno::UNO_QUERY_THROW);
241 catch (const Exception&)
243 // TODO: put a dialog telling about the need to install
244 // xslt2-transformer extension here
245 SAL_WARN("filter.xslt", "could not create XSLT 2.0 transformer");
246 throw;
249 else
251 xTransformer = xslt::XSLTTransformer::create(m_xContext, rArgs);
254 return xTransformer;
257 void
258 XSLTFilterStreamListener::started()
260 m_rParent.m_cTransformed.reset();
262 void
263 XSLTFilterStreamListener::error(const Any& a)
265 SAL_WARN("filter.xslt", "XSLTFilter::error was called: " << exceptionToString(a));
266 m_rParent.m_bError = true;
267 m_rParent.m_cTransformed.set();
269 void
270 XSLTFilterStreamListener::closed()
272 m_rParent.m_cTransformed.set();
274 void
275 XSLTFilterStreamListener::terminated()
277 m_rParent.m_bTerminated = true;
278 m_rParent.m_cTransformed.set();
281 OUString
282 XSLTFilter::rel2abs(const OUString& s)
285 css::uno::Reference<XStringSubstitution>
286 subs(css::util::PathSubstitution::create(m_xContext));
287 OUString aWorkingDir(subs->getSubstituteVariableValue( "$(progurl)" ));
288 INetURLObject aObj(aWorkingDir);
289 aObj.setFinalSlash();
290 bool bWasAbsolute;
291 INetURLObject aURL = aObj.smartRel2Abs(s, bWasAbsolute, false,
292 INetURLObject::EncodeMechanism::WasEncoded, RTL_TEXTENCODING_UTF8, true);
293 return aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
296 sal_Bool
297 XSLTFilter::importer(const Sequence<PropertyValue>& aSourceData,
298 const css::uno::Reference<XDocumentHandler>& xHandler, const Sequence<
299 OUString>& msUserData)
301 if (msUserData.getLength() < 5)
302 return false;
304 OUString udStyleSheet = rel2abs(msUserData[4]);
306 // get information from media descriptor
307 // the input stream that represents the imported file
308 // is most important here since we need to supply it to
309 // the sax parser that drives the supplied document handler
310 OUString aName, aURL;
311 css::uno::Reference<XInputStream> xInputStream;
312 css::uno::Reference<XInteractionHandler> xInterActionHandler;
313 for (const auto& sourceDataItem : aSourceData)
315 aName = sourceDataItem.Name;
316 Any value = sourceDataItem.Value;
317 if ( aName == "InputStream" )
318 value >>= xInputStream;
319 else if ( aName == "URL" )
320 value >>= aURL;
321 else if ( aName == "InteractionHandler" )
322 value >>= xInterActionHandler;
324 OSL_ASSERT(xInputStream.is());
325 if (!xInputStream.is())
326 return false;
328 // create transformer
329 Sequence<Any> args{ Any(NamedValue("StylesheetURL", Any(expandUrl(udStyleSheet)))),
330 Any(NamedValue("SourceURL", Any(aURL))),
331 Any(NamedValue("SourceBaseURL", Any(INetURLObject(aURL).getBase()))) };
332 m_tcontrol = impl_createTransformer(msUserData[1], args);
334 OSL_ASSERT(xHandler.is());
335 OSL_ASSERT(xInputStream.is());
336 OSL_ASSERT(m_tcontrol.is());
337 if (xHandler.is() && xInputStream.is() && m_tcontrol.is())
341 css::uno::Reference<css::io::XSeekable> xSeek(xInputStream, UNO_QUERY);
342 if (xSeek.is())
343 xSeek->seek(0);
345 // we want to be notified when the processing is done...
346 m_tcontrol->addListener(new XSLTFilterStreamListener(*this));
348 // connect input to transformer
349 m_tcontrol->setInputStream(xInputStream);
351 // create pipe
352 css::uno::Reference<XOutputStream> pipeout =
353 Pipe::create(m_xContext);
354 css::uno::Reference<XInputStream> pipein(pipeout, UNO_QUERY);
356 //connect transformer to pipe
357 m_tcontrol->setOutputStream(pipeout);
359 // connect pipe to sax parser
360 InputSource aInput;
361 aInput.sSystemId = aURL;
362 aInput.sPublicId = aURL;
363 aInput.aInputStream = pipein;
365 css::uno::Reference< css::xml::sax::XFastParser > xFastParser(
366 xHandler, css::uno::UNO_QUERY );
368 // transform
369 m_tcontrol->start();
370 TimeValue timeout = { TRANSFORMATION_TIMEOUT_SEC, 0};
371 osl::Condition::Result result(m_cTransformed.wait(&timeout));
372 while (osl::Condition::result_timeout == result) {
373 if (xInterActionHandler.is()) {
374 Sequence<Any> excArgs(0);
375 css::ucb::InteractiveAugmentedIOException exc(
376 "Timeout!",
377 getXWeak(),
378 InteractionClassification_ERROR,
379 css::ucb::IOErrorCode_GENERAL,
380 excArgs);
381 Any r;
382 r <<= exc;
383 rtl::Reference<::comphelper::OInteractionRequest> pRequest = new ::comphelper::OInteractionRequest(r);
384 rtl::Reference<::comphelper::OInteractionRetry> pRetry = new ::comphelper::OInteractionRetry;
385 rtl::Reference<::comphelper::OInteractionAbort> pAbort = new ::comphelper::OInteractionAbort;
386 pRequest->addContinuation(pRetry);
387 pRequest->addContinuation(pAbort);
388 xInterActionHandler->handle(pRequest);
389 if (pAbort->wasSelected()) {
390 m_bError = true;
391 m_cTransformed.set();
394 result = m_cTransformed.wait(&timeout);
396 if (!m_bError) {
397 if( xFastParser.is() )
398 xFastParser->parseStream( aInput );
399 else
401 // create SAX parser that will read the document file
402 // and provide events to xHandler passed to this call
403 css::uno::Reference<XParser> xSaxParser = Parser::create(m_xContext);
404 // set doc handler
405 xSaxParser->setDocumentHandler(xHandler);
406 xSaxParser->parseStream( aInput );
409 m_tcontrol->terminate();
410 return !m_bError;
412 catch( const Exception& )
414 // something went wrong
415 TOOLS_WARN_EXCEPTION("filter.xslt", "");
416 return false;
419 else
421 return false;
425 sal_Bool
426 XSLTFilter::importer(const Sequence<PropertyValue>& aSourceData,
427 const css::uno::Reference<XFastParser>& xFastParser, const Sequence<
428 OUString>& msUserData)
430 if (msUserData.getLength() < 5)
431 return false;
433 OUString udStyleSheet = rel2abs(msUserData[4]);
435 // get information from media descriptor
436 // the input stream that represents the imported file
437 // is most important here since we need to supply it to
438 // the sax parser that drives the supplied document handler
439 sal_Int32 nLength = aSourceData.getLength();
440 OUString aName, aURL;
441 css::uno::Reference<XInputStream> xInputStream;
442 css::uno::Reference<XInteractionHandler> xInterActionHandler;
443 for (sal_Int32 i = 0; i < nLength; i++)
445 aName = aSourceData[i].Name;
446 Any value = aSourceData[i].Value;
447 if ( aName == "InputStream" )
448 value >>= xInputStream;
449 else if ( aName == "URL" )
450 value >>= aURL;
451 else if ( aName == "InteractionHandler" )
452 value >>= xInterActionHandler;
454 OSL_ASSERT(xInputStream.is());
455 if (!xInputStream.is())
456 return false;
458 // create transformer
459 Sequence<Any> args{ Any(NamedValue("StylesheetURL", Any(expandUrl(udStyleSheet)))),
460 Any(NamedValue("SourceURL", Any(aURL))),
461 Any(NamedValue("SourceBaseURL", Any(INetURLObject(aURL).getBase()))) };
462 m_tcontrol = impl_createTransformer(msUserData[1], args);
464 assert(xFastParser.is());
465 OSL_ASSERT(xInputStream.is());
466 OSL_ASSERT(m_tcontrol.is());
467 if (xFastParser.is() && xInputStream.is() && m_tcontrol.is())
471 css::uno::Reference<css::io::XSeekable> xSeek(xInputStream, UNO_QUERY);
472 if (xSeek.is())
473 xSeek->seek(0);
475 // we want to be notified when the processing is done...
476 m_tcontrol->addListener(new XSLTFilterStreamListener(*this));
478 // connect input to transformer
479 m_tcontrol->setInputStream(xInputStream);
481 // create pipe
482 css::uno::Reference<XOutputStream> pipeout =
483 Pipe::create(m_xContext);
484 css::uno::Reference<XInputStream> pipein(pipeout, UNO_QUERY);
486 //connect transformer to pipe
487 m_tcontrol->setOutputStream(pipeout);
489 // connect pipe to sax parser
490 InputSource aInput;
491 aInput.sSystemId = aURL;
492 aInput.sPublicId = aURL;
493 aInput.aInputStream = pipein;
495 // transform
496 m_tcontrol->start();
497 TimeValue timeout = { TRANSFORMATION_TIMEOUT_SEC, 0};
498 osl::Condition::Result result(m_cTransformed.wait(&timeout));
499 while (osl::Condition::result_timeout == result) {
500 if (xInterActionHandler.is()) {
501 Sequence<Any> excArgs(0);
502 css::ucb::InteractiveAugmentedIOException exc(
503 "Timeout!",
504 getXWeak(),
505 InteractionClassification_ERROR,
506 css::ucb::IOErrorCode_GENERAL,
507 excArgs);
508 Any r;
509 r <<= exc;
510 rtl::Reference<::comphelper::OInteractionRequest> pRequest = new ::comphelper::OInteractionRequest(r);
511 rtl::Reference<::comphelper::OInteractionRetry> pRetry = new ::comphelper::OInteractionRetry;
512 rtl::Reference<::comphelper::OInteractionAbort> pAbort = new ::comphelper::OInteractionAbort;
513 pRequest->addContinuation(pRetry);
514 pRequest->addContinuation(pAbort);
515 xInterActionHandler->handle(pRequest);
516 if (pAbort->wasSelected()) {
517 m_bError = true;
518 m_cTransformed.set();
521 result = m_cTransformed.wait(&timeout);
523 if (!m_bError)
524 xFastParser->parseStream( aInput );
525 m_tcontrol->terminate();
526 return !m_bError;
528 catch( const Exception& )
530 // something went wrong
531 TOOLS_WARN_EXCEPTION("filter.xslt", "");
532 return false;
535 else
537 return false;
541 sal_Bool
542 XSLTFilter::exporter(const Sequence<PropertyValue>& aSourceData,
543 const Sequence<OUString>& msUserData)
545 if (msUserData.getLength() < 6)
546 return false;
548 // get interesting values from user data
549 OUString udStyleSheet = rel2abs(msUserData[5]);
551 // read source data
552 // we are especially interested in the output stream
553 // since that is where our xml-writer will push the data
554 // from its data-source interface
555 OUString aName, sURL;
556 OUString aDoctypePublic;
557 // css::uno::Reference<XOutputStream> rOutputStream;
558 sal_Int32 nLength = aSourceData.getLength();
559 for (sal_Int32 i = 0; i < nLength; i++)
561 aName = aSourceData[i].Name;
562 if ( aName == "DocType_Public" )
563 aSourceData[i].Value >>= aDoctypePublic;
564 else if ( aName == "OutputStream" )
565 aSourceData[i].Value >>= m_rOutputStream;
566 else if ( aName == "URL" )
567 aSourceData[i].Value >>= sURL;
570 if (!getDelegate().is())
572 // get the document writer
573 setDelegate(css::uno::Reference<XExtendedDocumentHandler>(
574 Writer::create(m_xContext),
575 UNO_QUERY_THROW));
578 // create transformer
579 INetURLObject ineturl(sURL);
580 ineturl.removeSegment();
581 m_aExportBaseUrl = ineturl.GetMainURL(INetURLObject::DecodeMechanism::NONE);
582 Sequence<Any> args{ Any(NamedValue("StylesheetURL", Any(expandUrl(udStyleSheet)))),
583 Any(NamedValue("TargetURL", Any(sURL))),
584 Any(NamedValue("DoctypePublic", Any(aDoctypePublic))),
585 Any(NamedValue("TargetBaseURL", Any(m_aExportBaseUrl))) };
586 m_tcontrol = impl_createTransformer(msUserData[1], args);
588 OSL_ASSERT(m_rOutputStream.is());
589 OSL_ASSERT(m_tcontrol.is());
590 if (m_tcontrol.is() && m_rOutputStream.is())
592 // we want to be notified when the processing is done...
593 m_tcontrol->addListener(new XSLTFilterStreamListener(*this));
595 // create pipe
596 css::uno::Reference<XOutputStream> pipeout =
597 Pipe::create(m_xContext);
598 css::uno::Reference<XInputStream> pipein(pipeout, UNO_QUERY);
600 // connect sax writer to pipe
601 css::uno::Reference<XActiveDataSource> xmlsource(getDelegate(),
602 UNO_QUERY);
603 xmlsource->setOutputStream(pipeout);
605 // connect pipe to transformer
606 m_tcontrol->setInputStream(pipein);
608 // connect transformer to output
609 m_tcontrol->setOutputStream(m_rOutputStream);
611 // we will start receiving events after returning 'true'.
612 // we will start the transformation as soon as we receive the startDocument
613 // event.
614 return true;
616 else
618 return false;
622 // for the DocumentHandler implementation, we just proxy the
623 // events to the XML writer that we created upon the output stream
624 // that was provided by the XMLFilterAdapter
625 void
626 XSLTFilter::startDocument()
628 ExtendedDocumentHandlerAdapter::startDocument();
629 m_tcontrol->start();
632 void
633 XSLTFilter::endDocument()
635 ExtendedDocumentHandlerAdapter::endDocument();
636 // wait for the transformer to finish
637 m_cTransformed.wait();
638 m_tcontrol->terminate();
639 if (m_bError || m_bTerminated)
640 throw RuntimeException();
646 // Component management
648 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
649 filter_XSLTFilter_get_implementation(
650 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
652 return cppu::acquire(new XSLT::XSLTFilter(context));
655 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */