/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * **************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_filter.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uof2splitter.hxx" #include "uof2storage.hxx" #include "uof2merge.hxx" using namespace ::rtl; using namespace ::cppu; using namespace ::osl; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::io; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::registry; using namespace ::com::sun::star::xml; using namespace ::com::sun::star::xml::sax; using namespace ::com::sun::star::util; namespace XSLT { class XSLTFilter : public WeakImplHelper4< XImportFilter, XExportFilter, XDocumentHandler, XStreamListener> { public: // ctor... XSLTFilter( const Reference< XMultiServiceFactory > &r ); // XStreamListener virtual void SAL_CALL error(const Any& a) throw (RuntimeException); virtual void SAL_CALL closed() throw (RuntimeException); virtual void SAL_CALL terminated() throw (RuntimeException); virtual void SAL_CALL started() throw (RuntimeException); virtual void SAL_CALL disposing(const EventObject& e) throw (RuntimeException); // XImportFilter virtual sal_Bool SAL_CALL importer( const Sequence& aSourceData, const Reference& xHandler, const Sequence& msUserData) throw(RuntimeException); // XExportFilter virtual sal_Bool SAL_CALL exporter( const Sequence& aSourceData, const Sequence& msUserData) throw(RuntimeException); // XDocumentHandler virtual void SAL_CALL startDocument() throw (SAXException,RuntimeException); virtual void SAL_CALL endDocument() throw (SAXException, RuntimeException); virtual void SAL_CALL startElement(const OUString& str, const Reference& attriblist) throw (SAXException,RuntimeException); virtual void SAL_CALL endElement(const OUString& str) throw (SAXException, RuntimeException); virtual void SAL_CALL characters(const OUString& str) throw (SAXException, RuntimeException); virtual void SAL_CALL ignorableWhitespace(const OUString& str) throw (SAXException, RuntimeException); virtual void SAL_CALL processingInstruction(const OUString& str, const OUString& str2) throw (com::sun::star::xml::sax::SAXException,RuntimeException); virtual void SAL_CALL setDocumentLocator(const Reference& doclocator) throw (SAXException,RuntimeException); private: // the UNO ServiceFactory Reference< XMultiServiceFactory > m_rServiceFactory; // DocumentHandler interface of the css::xml::sax::Writer service Reference < XExtendedDocumentHandler > m_rDocumentHandler; Reference < XOutputStream > m_rOutputStream; // controls pretty-printing sal_Bool m_bPrettyPrint; Reference< XActiveDataControl > m_tcontrol; oslCondition m_cTransformed; sal_Bool m_bTerminated; sal_Bool m_bError; OUString m_aExportBaseUrl; OUString m_aOldBaseUrl; OUString rel2abs(const OUString&); OUString expandUrl(const OUString&); // for support of UOF v2.0 Reference< XActiveDataControl > m_splitControl; Reference< XStream > m_rStream; bool isUOF2ExportStyleSheet( const OUString& rExportStyleSheet ); }; XSLTFilter::XSLTFilter( const Reference< XMultiServiceFactory > &r ) : m_rServiceFactory( r ) , m_rDocumentHandler() , m_rOutputStream() , m_bPrettyPrint( sal_True ) , m_tcontrol() , m_cTransformed() , m_bTerminated( sal_False ) , m_bError( sal_False ) , m_aExportBaseUrl() , m_aOldBaseUrl() , m_splitControl() , m_rStream() { m_cTransformed = osl_createCondition(); } void XSLTFilter::disposing(const EventObject& ) throw (RuntimeException) { } ::rtl::OUString XSLTFilter::expandUrl( const ::rtl::OUString& sUrl ) { ::rtl::OUString sExpandedUrl; try { Reference< XComponentContext > xContext; Reference< XPropertySet > xProps( m_rServiceFactory, UNO_QUERY_THROW ); xContext.set( xProps->getPropertyValue( ::rtl::OUString::createFromAscii( "DefaultContext" ) ), UNO_QUERY_THROW ); Reference< XMacroExpander > xMacroExpander( xContext->getValueByName( ::rtl::OUString::createFromAscii( "/singletons/com.sun.star.util.theMacroExpander" ) ), UNO_QUERY_THROW ); sExpandedUrl = xMacroExpander->expandMacros(sUrl); sal_Int32 nPos = sExpandedUrl.indexOf(::rtl::OUString::createFromAscii("vnd.sun.star.expand:")); if ( nPos != -1 ) sExpandedUrl = sExpandedUrl.copy(nPos+20); } catch (Exception&) {} return sExpandedUrl; } void XSLTFilter::started() throw (RuntimeException) { osl_resetCondition(m_cTransformed); } void XSLTFilter::error(const Any& a) throw (RuntimeException) { Exception e; if ( a >>= e) { OString aMessage("XSLTFilter::error was called: "); aMessage += OUStringToOString(e.Message, RTL_TEXTENCODING_ASCII_US); OSL_ENSURE(sal_False, aMessage); } m_bError = sal_True; osl_setCondition(m_cTransformed); } void XSLTFilter::closed() throw (RuntimeException) { osl_setCondition(m_cTransformed); } void XSLTFilter::terminated() throw (RuntimeException) { m_bTerminated = sal_True; osl_setCondition(m_cTransformed); } OUString XSLTFilter::rel2abs(const OUString& s) { Reference< XStringSubstitution > subs(m_rServiceFactory->createInstance( OUString::createFromAscii("com.sun.star.util.PathSubstitution")), UNO_QUERY); OUString aWorkingDir = subs->getSubstituteVariableValue(OUString::createFromAscii("$(progurl)")); INetURLObject aObj( aWorkingDir ); aObj.setFinalSlash(); bool bWasAbsolute; INetURLObject aURL = aObj.smartRel2Abs( s, bWasAbsolute, false, INetURLObject::WAS_ENCODED, RTL_TEXTENCODING_UTF8, true ); return aURL.GetMainURL(INetURLObject::NO_DECODE); } sal_Bool XSLTFilter::importer( const Sequence& aSourceData, const Reference& xHandler, const Sequence& msUserData) throw (RuntimeException) { if ( msUserData.getLength() < 5 ) return sal_False; const OUString udStyleSheet = rel2abs( msUserData[4] ); // get information from media descriptor // the input stream that represents the imported file // is most important here since we need to supply it to // the sax parser that drives the supplied document handler const sal_Int32 nLength = aSourceData.getLength(); OUString aFileName; OUString aURL; Reference< XInputStream > xInputStream; for ( sal_Int32 i = 0 ; i < nLength; i++) { const OUString aName = aSourceData[i].Name; if ( aName.equalsAscii( "InputStream" ) ) aSourceData[i].Value >>= xInputStream; else if ( aName.equalsAscii( "FileName" ) ) aSourceData[i].Value >>= aFileName; else if ( aName.equalsAscii( "URL" ) ) aSourceData[i].Value >>= aURL; } OSL_ASSERT(xInputStream.is()); if ( !xInputStream.is() ) return sal_False; // create SAX parser that will read the document file // and provide events to xHandler passed to this call Reference < XParser > xSaxParser( m_rServiceFactory->createInstance( OUString::createFromAscii("com.sun.star.xml.sax.Parser")), UNO_QUERY ); OSL_ASSERT(xSaxParser.is()); if( !xSaxParser.is() ) return sal_False; // create transformer Sequence< Any > args(3); NamedValue nv; nv.Name = OUString::createFromAscii("StylesheetURL"); nv.Value <<= expandUrl(udStyleSheet); args[0] <<= nv; nv.Name = OUString::createFromAscii("SourceURL"); nv.Value <<= aURL; args[1] <<= nv; nv.Name = OUString::createFromAscii("SourceBaseURL"); nv.Value <<= OUString(INetURLObject(aURL).getBase()); args[2] <<= nv; m_tcontrol = Reference< XActiveDataControl >(m_rServiceFactory->createInstanceWithArguments( OUString::createFromAscii("com.sun.star.comp.JAXTHelper"), args), UNO_QUERY); OSL_ASSERT(xHandler.is()); OSL_ASSERT(xInputStream.is()); OSL_ASSERT(m_tcontrol.is()); if (xHandler.is() && xInputStream.is() && m_tcontrol.is()) { try { // we want to be notified when the processing is done... m_tcontrol->addListener(Reference< XStreamListener >(this)); // connect input to transformer Reference< XActiveDataSink > tsink(m_tcontrol, UNO_QUERY); // UOF v2 import UOF2Storage aUOF2Storage( m_rServiceFactory, xInputStream ); if ( aUOF2Storage.isValidUOF2Doc() ) { UOF2Merge aUOF2Merge( aUOF2Storage, m_rServiceFactory ); aUOF2Merge.merge(); tsink->setInputStream( aUOF2Merge.getMergedInStream() ); } else { tsink->setInputStream( xInputStream ); } // create pipe Reference< XOutputStream > pipeout(m_rServiceFactory->createInstance( OUString::createFromAscii("com.sun.star.io.Pipe")), UNO_QUERY); Reference< XInputStream > pipein(pipeout, UNO_QUERY); //connect transformer to pipe Reference< XActiveDataSource > tsource(m_tcontrol, UNO_QUERY); tsource->setOutputStream(pipeout); // connect pipe to sax parser InputSource aInput; aInput.sSystemId = aURL; aInput.sPublicId = aURL; aInput.aInputStream = pipein; // set doc handler xSaxParser->setDocumentHandler(xHandler); // transform m_tcontrol->start(); // osl_waitCondition(m_cTransformed, 0); if (!m_bError && !m_bTerminated) { // parse the transformed XML buffered in the pipe xSaxParser->parseStream(aInput); osl_waitCondition(m_cTransformed, 0); return sal_True; } else { return sal_False; } } #if OSL_DEBUG_LEVEL > 0 catch( Exception& exc) #else catch( Exception& ) #endif { // something went wrong OSL_ENSURE(0, OUStringToOString(exc.Message, RTL_TEXTENCODING_ASCII_US).getStr()); return sal_False; } } else { return sal_False; } } bool XSLTFilter::isUOF2ExportStyleSheet( const OUString& rExportStyleSheet ) { bool bIsUOFDocumentType = false; if ( rExportStyleSheet.endsWithAsciiL( "uof.xsl", 7 ) ) { bIsUOFDocumentType = true; } return bIsUOFDocumentType; } sal_Bool XSLTFilter::exporter( const Sequence& aSourceData, const Sequence& msUserData) throw (RuntimeException) { if ( msUserData.getLength() < 6 ) return sal_False; // get interesting values from user data const OUString udStyleSheet = rel2abs( msUserData[5] ); // read source data // we are especially interested in the output stream // since that is where our xml-writer will push the data // from its data-source interface OUString sURL; sal_Bool bIndent = sal_False; OUString aDoctypePublic; OUString aDoctypeSystem; { const sal_Int32 nLength = aSourceData.getLength(); for ( sal_Int32 i = 0; i < nLength; i++ ) { const OUString aName = aSourceData[i].Name; if ( aName.equalsAscii( "Indent" ) ) aSourceData[i].Value >>= bIndent; if ( aName.equalsAscii( "DocType_Public" ) ) aSourceData[i].Value >>= aDoctypePublic; if ( aName.equalsAscii( "DocType_System" ) ) aSourceData[i].Value >>= aDoctypeSystem; if ( aName.equalsAscii( "OutputStream" ) ) aSourceData[i].Value >>= m_rOutputStream; else if ( aName.equalsAscii( "URL" ) ) aSourceData[i].Value >>= sURL; // UOF v2.0 export, get Stream for constructing UOF2Storage if ( aName.equalsAscii( "StreamForOutput" ) ) aSourceData[i].Value >>= m_rStream; } } if (!m_rDocumentHandler.is()) { // get the document writer m_rDocumentHandler = Reference( m_rServiceFactory->createInstance( OUString::createFromAscii("com.sun.star.xml.sax.Writer") ), UNO_QUERY); } // create transformer Sequence< Any > args(4); NamedValue nv; nv.Name = OUString::createFromAscii("StylesheetURL"); nv.Value <<= expandUrl(udStyleSheet); args[0] <<= nv; nv.Name = OUString::createFromAscii("TargetURL"); nv.Value <<= sURL; args[1] <<= nv; nv.Name = OUString::createFromAscii("DoctypeSystem"); nv.Value <<= aDoctypeSystem; args[2] <<= nv; nv.Name = OUString::createFromAscii("DoctypePublic"); nv.Value <<= aDoctypePublic; args[3] <<= nv; nv.Name = OUString::createFromAscii("TargetBaseURL"); INetURLObject ineturl(sURL); ineturl.removeSegment(); m_aExportBaseUrl = ineturl.GetMainURL(INetURLObject::NO_DECODE); nv.Value <<= m_aExportBaseUrl; args[3] <<= nv; m_tcontrol = Reference< XActiveDataControl >(m_rServiceFactory->createInstanceWithArguments( OUString::createFromAscii("com.sun.star.comp.JAXTHelper"), args), UNO_QUERY); OSL_ASSERT(m_rDocumentHandler.is()); OSL_ASSERT(m_rOutputStream.is()); OSL_ASSERT(m_tcontrol.is()); if (m_tcontrol.is() && m_rOutputStream.is() && m_rDocumentHandler.is()) { // we want to be notified when the processing is done... m_tcontrol->addListener(Reference< XStreamListener >(this)); // create pipe Reference< XOutputStream > pipeout(m_rServiceFactory->createInstance( OUString::createFromAscii("com.sun.star.io.Pipe")), UNO_QUERY); Reference< XInputStream > pipein(pipeout, UNO_QUERY); // connect sax writer to pipe Reference< XActiveDataSource > xmlsource(m_rDocumentHandler, UNO_QUERY); xmlsource->setOutputStream(pipeout); // connect pipe to transformer Reference< XActiveDataSink > tsink(m_tcontrol, UNO_QUERY); tsink->setInputStream(pipein); // connect transformer to output Reference< XActiveDataSource > tsource( m_tcontrol, UNO_QUERY ); if ( isUOF2ExportStyleSheet( udStyleSheet ) ) { // special handling for UOF 2 if ( !m_rStream.is() ) { return sal_False; } // creating pipe2 Reference< XOutputStream > x_Pipeout( m_rServiceFactory->createInstance( OUString::createFromAscii( "com.sun.star.io.Pipe" ) ), UNO_QUERY ); Reference< XInputStream > x_Pipein( x_Pipeout, UNO_QUERY ); // connect transformer to pipe2 tsource->setOutputStream( x_Pipeout ); UOF2Splitter* pSplitter = new UOF2Splitter( m_rServiceFactory, sURL ); m_splitControl = Reference< XActiveDataControl >( static_cast< cppu::OWeakObject* >( pSplitter ), UNO_QUERY ); // connect pipe2 to splitter Reference< XActiveDataSink > splitsink( m_splitControl, UNO_QUERY ); splitsink->setInputStream( x_Pipein ); // connect splitter to output Reference< XActiveDataStreamer > splitout( m_splitControl, UNO_QUERY ); splitout->setStream( m_rStream ); m_rOutputStream = m_rStream->getOutputStream(); } else { tsource->setOutputStream( m_rOutputStream ); } // we will start receiving events after returning 'true'. // we will start the transformation as soon as we receive the startDocument event. return sal_True; } else { return sal_False; } } // for the DocumentHandler implementation, we just proxy the the // events to the XML writer that we created upon the output stream // that was provided by the XMLFilterAdapter void XSLTFilter::startDocument() throw (SAXException,RuntimeException){ OSL_ASSERT(m_rDocumentHandler.is()); m_rDocumentHandler->startDocument(); m_tcontrol->start(); } void XSLTFilter::endDocument() throw (SAXException, RuntimeException){ OSL_ASSERT(m_rDocumentHandler.is()); m_rDocumentHandler->endDocument(); // m_splitControl only set for UOF 2 if ( m_splitControl.is() ) { // when the inputStream(outputStream of filter) was closed, start to parse it. m_splitControl->start(); } // wait for the transformer to finish osl_waitCondition(m_cTransformed, 0); if (!m_bError && !m_bTerminated) { return; } else { throw RuntimeException(); } } void XSLTFilter::startElement(const OUString& str, const Reference& attriblist) throw (SAXException, RuntimeException) { OSL_ASSERT(m_rDocumentHandler.is()); // SvXMLAttributeList* _attriblist=SvXMLAttributeList::getImplementation(attriblist); m_rDocumentHandler->startElement(str, attriblist); } void XSLTFilter::endElement(const OUString& str) throw (SAXException, RuntimeException) { OSL_ASSERT(m_rDocumentHandler.is()); m_rDocumentHandler->endElement(str); } void XSLTFilter::characters(const OUString& str) throw (SAXException, RuntimeException) { OSL_ASSERT(m_rDocumentHandler.is()); m_rDocumentHandler->characters(str); } void XSLTFilter::ignorableWhitespace(const OUString& str) throw (SAXException, RuntimeException) { OSL_ASSERT(m_rDocumentHandler.is()); if (!m_bPrettyPrint) return; m_rDocumentHandler->ignorableWhitespace(str); } void XSLTFilter::processingInstruction(const OUString& str, const OUString& str2) throw (SAXException, RuntimeException) { OSL_ASSERT(m_rDocumentHandler.is()); m_rDocumentHandler->processingInstruction(str, str2); } void XSLTFilter::setDocumentLocator(const Reference& doclocator) throw (SAXException, RuntimeException) { OSL_ASSERT(m_rDocumentHandler.is()); m_rDocumentHandler->setDocumentLocator(doclocator); } // -------------------------------------- // Component management // -------------------------------------- #define SERVICE_NAME "com.sun.star.documentconversion.XSLTFilter" #define IMPLEMENTATION_NAME "com.sun.star.comp.documentconversion.XSLTFilter" static Reference< XInterface > SAL_CALL CreateInstance( const Reference< XMultiServiceFactory > &r) { return Reference< XInterface >(( OWeakObject *)new XSLTFilter(r)); } static Sequence< OUString > getSupportedServiceNames() { static Sequence < OUString > *pNames = 0; if( ! pNames ) { MutexGuard guard( Mutex::getGlobalMutex() ); if( !pNames ) { static Sequence< OUString > seqNames(1); seqNames.getArray()[0] = OUString::createFromAscii(SERVICE_NAME); pNames = &seqNames; } } return *pNames; } } using namespace XSLT; extern "C" { void SAL_CALL component_getImplementationEnvironment( const sal_Char ** ppEnvTypeName, uno_Environment ** /* ppEnv */ ) { *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME; } sal_Bool SAL_CALL component_writeInfo(void * /* pServiceManager */, void * pRegistryKey ) { if (pRegistryKey) { try { Reference< XRegistryKey > xNewKey( reinterpret_cast< XRegistryKey * >( pRegistryKey )->createKey( OUString::createFromAscii( "/" IMPLEMENTATION_NAME "/UNO/SERVICES" ) ) ); const Sequence< OUString > & rSNL = getSupportedServiceNames(); const OUString * pArray = rSNL.getConstArray(); for ( sal_Int32 nPos = rSNL.getLength(); nPos--; ) xNewKey->createKey( pArray[nPos] ); return sal_True; } catch (InvalidRegistryException &) { OSL_ENSURE( sal_False, "### InvalidRegistryException!" ); } } return sal_False; } void * SAL_CALL component_getFactory( const sal_Char * pImplName, void * pServiceManager, void * /* pRegistryKey */ ) { void * pRet = 0; if (pServiceManager && rtl_str_compare( pImplName, IMPLEMENTATION_NAME ) == 0) { Reference< XSingleServiceFactory > xFactory( createSingleFactory( reinterpret_cast< XMultiServiceFactory * >( pServiceManager ), OUString::createFromAscii( pImplName ), CreateInstance, getSupportedServiceNames() ) ); if (xFactory.is()) { xFactory->acquire(); pRet = xFactory.get(); } } return pRet; } } // extern "C"