/************************************************************** * * 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_dbmm.hxx" #include "dbmm_global.hrc" #include "dbmm_module.hxx" #include "dbmm_types.hxx" #include "docinteraction.hxx" #include "migrationengine.hxx" #include "migrationerror.hxx" #include "migrationprogress.hxx" #include "migrationlog.hxx" #include "progresscapture.hxx" #include "progressmixer.hxx" /** === begin UNO includes === **/ #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 /** === end UNO includes === **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_DOC_PROGRESS_RANGE 100000 //........................................................................ namespace dbmm { //........................................................................ /** === begin UNO using === **/ using ::com::sun::star::uno::Reference; using ::com::sun::star::uno::XInterface; using ::com::sun::star::uno::UNO_QUERY; using ::com::sun::star::uno::UNO_QUERY_THROW; using ::com::sun::star::uno::UNO_SET_THROW; using ::com::sun::star::uno::Exception; using ::com::sun::star::uno::RuntimeException; using ::com::sun::star::uno::Any; using ::com::sun::star::uno::makeAny; using ::com::sun::star::sdb::XOfficeDatabaseDocument; using ::com::sun::star::sdb::XFormDocumentsSupplier; using ::com::sun::star::sdb::XReportDocumentsSupplier; using ::com::sun::star::container::XNameAccess; using ::com::sun::star::uno::Sequence; using ::com::sun::star::util::XCloseable; using ::com::sun::star::util::CloseVetoException; using ::com::sun::star::lang::XComponent; using ::com::sun::star::frame::XModel; using ::com::sun::star::frame::XComponentLoader; using ::com::sun::star::ucb::XCommandProcessor; using ::com::sun::star::ucb::XContent; using ::com::sun::star::ucb::Command; using ::com::sun::star::embed::XComponentSupplier; using ::com::sun::star::task::XStatusIndicator; using ::com::sun::star::embed::XStorage; using ::com::sun::star::document::XStorageBasedDocument; using ::com::sun::star::embed::XTransactedObject; using ::com::sun::star::frame::XStorable; using ::com::sun::star::embed::XEmbedPersist; using ::com::sun::star::script::DocumentDialogLibraryContainer; using ::com::sun::star::script::DocumentScriptLibraryContainer; using ::com::sun::star::script::XStorageBasedLibraryContainer; using ::com::sun::star::document::XEmbeddedScripts; using ::com::sun::star::container::XNameContainer; using ::com::sun::star::document::XEventsSupplier; using ::com::sun::star::container::XNameReplace; using com::sun::star::uri::UriReferenceFactory; using com::sun::star::uri::XUriReferenceFactory; using com::sun::star::uri::XVndSunStarScriptUrlReference; using ::com::sun::star::form::XFormsSupplier; using ::com::sun::star::drawing::XDrawPageSupplier; using ::com::sun::star::drawing::XDrawPagesSupplier; using ::com::sun::star::drawing::XDrawPage; using ::com::sun::star::drawing::XDrawPages; using ::com::sun::star::container::XIndexAccess; using ::com::sun::star::script::XEventAttacherManager; using ::com::sun::star::script::ScriptEventDescriptor; using ::com::sun::star::script::XLibraryContainerPassword; using ::com::sun::star::io::WrongFormatException; using ::com::sun::star::script::XScriptEventsSupplier; using ::com::sun::star::io::XInputStreamProvider; using ::com::sun::star::io::XInputStream; /** === end UNO using === **/ namespace ElementModes = ::com::sun::star::embed::ElementModes; // migration phases whose progresses are to be mixed into one progress #define PHASE_JAVASCRIPT 1 #define PHASE_BEANSHELL 2 #define PHASE_PYTHON 3 #define PHASE_JAVA 4 #define PHASE_BASIC 5 #define PHASE_DIALOGS 6 //==================================================================== //= SubDocument //==================================================================== struct SubDocument { Reference< XCommandProcessor > xCommandProcessor; Reference< XModel > xDocument; // valid only temporarily ::rtl::OUString sHierarchicalName; SubDocumentType eType; size_t nNumber; SubDocument( const Reference< XCommandProcessor >& _rxCommandProcessor, const ::rtl::OUString& _rName, const SubDocumentType _eType, const size_t _nNumber ) :xCommandProcessor( _rxCommandProcessor ) ,xDocument() ,sHierarchicalName( _rName ) ,eType( _eType ) ,nNumber( _nNumber ) { } }; typedef ::std::vector< SubDocument > SubDocuments; //==================================================================== //= helper //==================================================================== //-------------------------------------------------------------------- typedef ::utl::SharedUNOComponent< XStorage > SharedStorage; namespace { //---------------------------------------------------------------- static const ::rtl::OUString& lcl_getScriptsStorageName() { static const ::rtl::OUString s_sScriptsStorageName( RTL_CONSTASCII_USTRINGPARAM( "Scripts" ) ); return s_sScriptsStorageName; } //---------------------------------------------------------------- static const ::rtl::OUString& lcl_getScriptsSubStorageName( const ScriptType _eType ) { static const ::rtl::OUString s_sBeanShell ( RTL_CONSTASCII_USTRINGPARAM( "beanshell" ) ); static const ::rtl::OUString s_sJavaScript( RTL_CONSTASCII_USTRINGPARAM( "javascript" ) ); static const ::rtl::OUString s_sPython ( RTL_CONSTASCII_USTRINGPARAM( "python" ) ); // TODO: is this correct? static const ::rtl::OUString s_sJava ( RTL_CONSTASCII_USTRINGPARAM( "java" ) ); switch ( _eType ) { case eBeanShell: return s_sBeanShell; case eJavaScript: return s_sJavaScript; case ePython: return s_sPython; case eJava: return s_sJava; default: break; } OSL_ENSURE( false, "lcl_getScriptsSubStorageName: illegal type!" ); static ::rtl::OUString s_sEmpty; return s_sEmpty; } //---------------------------------------------------------------- static bool lcl_getScriptTypeFromLanguage( const ::rtl::OUString& _rLanguage, ScriptType& _out_rScriptType ) { struct LanguageMapping { const sal_Char* pAsciiLanguage; const ScriptType eScriptType; LanguageMapping( const sal_Char* _pAsciiLanguage, const ScriptType _eScriptType ) :pAsciiLanguage( _pAsciiLanguage ) ,eScriptType( _eScriptType ) { } } aLanguageMapping[] = { LanguageMapping( "JavaScript", eJavaScript ), LanguageMapping( "BeanShell", eBeanShell ), LanguageMapping( "Java", eJava ), LanguageMapping( "Python", ePython ), // TODO: is this correct? LanguageMapping( "Basic", eBasic ) }; for ( size_t i=0; i < sizeof( aLanguageMapping ) / sizeof( aLanguageMapping[0] ); ++i ) { if ( _rLanguage.equalsAscii( aLanguageMapping[i].pAsciiLanguage ) ) { _out_rScriptType = aLanguageMapping[i].eScriptType; return true; } } OSL_ENSURE( false, "lcl_getScriptTypeFromLanguage: unknown language!" ); return false; } //---------------------------------------------------------------- ::rtl::OUString lcl_getSubDocumentDescription( const SubDocument& _rDocument ) { ::rtl::OUString sObjectName = String( MacroMigrationResId( _rDocument.eType == eForm ? STR_FORM : STR_REPORT ) ); ::comphelper::string::searchAndReplaceAsciiI( sObjectName, "$name$", _rDocument.sHierarchicalName ); return sObjectName; } //---------------------------------------------------------------- static Any lcl_executeCommand_throw( const Reference< XCommandProcessor >& _rxCommandProc, const sal_Char* _pAsciiCommand ) { OSL_PRECOND( _rxCommandProc.is(), "lcl_executeCommand_throw: illegal object!" ); if ( !_rxCommandProc.is() ) return Any(); Command aCommand; aCommand.Name = ::rtl::OUString::createFromAscii( _pAsciiCommand ); return _rxCommandProc->execute( aCommand, _rxCommandProc->createCommandIdentifier(), NULL ); } //---------------------------------------------------------------- ::rtl::OUString lcl_getMimeType_nothrow( const Reference< XCommandProcessor >& _rxContent ) { ::rtl::OUString sMimeType; try { Reference< XContent > xContent( _rxContent, UNO_QUERY_THROW ); sMimeType = xContent->getContentType(); } catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); } return sMimeType; } //---------------------------------------------------------------- enum OpenDocResult { eOpenedDoc, eIgnoreDoc, eFailure }; //---------------------------------------------------------------- static OpenDocResult lcl_loadSubDocument_nothrow( SubDocument& _rDocument, const Reference< XStatusIndicator >& _rxProgress, MigrationLog& _rLogger ) { OSL_PRECOND( !_rDocument.xDocument.is(), "lcl_loadSubDocument_nothrow: already loaded!" ); try { ::comphelper::NamedValueCollection aLoadArgs; aLoadArgs.put( "Hidden", (sal_Bool)sal_True ); aLoadArgs.put( "StatusIndicator", _rxProgress ); Reference< XCommandProcessor > xCommandProcessor( _rDocument.xCommandProcessor, UNO_SET_THROW ); Command aCommand; aCommand.Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "openDesign" ) ); aCommand.Argument <<= aLoadArgs.getPropertyValues(); Reference< XComponent > xDocComponent( xCommandProcessor->execute( aCommand, xCommandProcessor->createCommandIdentifier(), NULL ), UNO_QUERY ); OSL_ENSURE( xDocComponent.is(), "lcl_loadSubDocument_nothrow: no component loaded!" ); _rDocument.xDocument.set( xDocComponent, UNO_QUERY_THROW ); } catch( const Exception& ) { Any aError( ::cppu::getCaughtException() ); bool bCausedByNewStyleReport = ( _rDocument.eType == eReport ) && ( aError.isExtractableTo( ::cppu::UnoType< WrongFormatException >::get() ) ) && ( lcl_getMimeType_nothrow( _rDocument.xCommandProcessor ).equalsAscii( "application/vnd.sun.xml.report" ) ); if ( bCausedByNewStyleReport ) { _rLogger.logRecoverable( MigrationError( ERR_NEW_STYLE_REPORT, lcl_getSubDocumentDescription( _rDocument ) ) ); return eIgnoreDoc; } else { _rLogger.logFailure( MigrationError( ERR_OPENING_SUB_DOCUMENT_FAILED, lcl_getSubDocumentDescription( _rDocument ), aError ) ); } } return _rDocument.xDocument.is() ? eOpenedDoc : eFailure; } //---------------------------------------------------------------- static bool lcl_unloadSubDocument_nothrow( SubDocument& _rDocument, MigrationLog& _rLogger ) { bool bSuccess = false; Any aException; try { OSL_VERIFY( lcl_executeCommand_throw( _rDocument.xCommandProcessor, "close" ) >>= bSuccess ); } catch( const Exception& ) { aException = ::cppu::getCaughtException(); } // log the failure, if any if ( !bSuccess ) { _rLogger.logFailure( MigrationError( ERR_CLOSING_SUB_DOCUMENT_FAILED, lcl_getSubDocumentDescription( _rDocument ), aException ) ); } _rDocument.xDocument.clear(); return bSuccess; } //---------------------------------------------------------------- bool lcl_commitStorage_nothrow( const Reference< XStorage >& _rxStorage ) { try { Reference< XTransactedObject > xTrans( _rxStorage, UNO_QUERY_THROW ); xTrans->commit(); } catch( const Exception& ) { return false; } return true; } //---------------------------------------------------------------- bool lcl_commitDocumentStorage_nothrow( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger ) { bool bSuccess = false; Any aException; try { Reference< XStorageBasedDocument > xStorageDoc( _rxDocument, UNO_QUERY_THROW ); Reference< XStorage > xDocStorage( xStorageDoc->getDocumentStorage(), UNO_QUERY_THROW ); bSuccess = lcl_commitStorage_nothrow( xDocStorage ); } catch( const Exception& ) { aException = ::cppu::getCaughtException(); } // log the failure, if any if ( !bSuccess ) { _rLogger.logFailure( MigrationError( ERR_STORAGE_COMMIT_FAILED, ::comphelper::DocumentInfo::getDocumentTitle( _rxDocument ), aException ) ); } return bSuccess; } //---------------------------------------------------------------- bool lcl_storeDocument_nothrow( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger ) { bool bSuccess = false; Any aException; try { Reference< XStorable > xStorable( _rxDocument, UNO_QUERY_THROW ); xStorable->store(); bSuccess = true; } catch( const Exception& ) { aException = ::cppu::getCaughtException(); } // log the failure, if any if ( !bSuccess ) { _rLogger.logFailure( MigrationError( ERR_STORING_DATABASEDOC_FAILED, aException ) ); } return bSuccess; } //---------------------------------------------------------------- bool lcl_storeEmbeddedDocument_nothrow( const SubDocument& _rDocument ) { try { lcl_executeCommand_throw( _rDocument.xCommandProcessor, "store" ); } catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return false; } return true; } } //==================================================================== //= DrawPageIterator //==================================================================== class DrawPageIterator { public: DrawPageIterator( const Reference< XModel >& _rxDocument ) :m_xDocument( _rxDocument ) ,m_nPageCount( 0 ) ,m_nCurrentPage( 0 ) { Reference< XDrawPageSupplier > xSingle( _rxDocument, UNO_QUERY ); Reference< XDrawPagesSupplier > xMulti( _rxDocument, UNO_QUERY ); if ( xSingle.is() ) { m_xSinglePage.set( xSingle->getDrawPage(), UNO_SET_THROW ); m_nPageCount = 1; } else if ( xMulti.is() ) { m_xMultiPages.set( xMulti->getDrawPages(), UNO_SET_THROW ); m_nPageCount = m_xMultiPages->getCount(); } } bool hasMore() const { return m_nCurrentPage < m_nPageCount; } Reference< XDrawPage > next() { Reference< XDrawPage > xNextPage; if ( m_xSinglePage.is() ) { xNextPage = m_xSinglePage; } else if ( m_xMultiPages.is() ) { xNextPage.set( m_xMultiPages->getByIndex( m_nCurrentPage ), UNO_QUERY_THROW ); } ++m_nCurrentPage; return xNextPage; } private: const Reference< XModel > m_xDocument; Reference< XDrawPage > m_xSinglePage; Reference< XDrawPages > m_xMultiPages; sal_Int32 m_nPageCount; sal_Int32 m_nCurrentPage; }; //==================================================================== //= FormComponentScripts //==================================================================== class FormComponentScripts { public: FormComponentScripts( const Reference< XInterface >& _rxComponent, const Reference< XEventAttacherManager >& _rxManager, const sal_Int32 _nIndex ) :m_xComponent( _rxComponent, UNO_SET_THROW ) ,m_xManager( _rxManager, UNO_SET_THROW ) ,m_nIndex( _nIndex ) { } Sequence< ScriptEventDescriptor > getEvents() const { return m_xManager->getScriptEvents( m_nIndex ); } void setEvents( const Sequence< ScriptEventDescriptor >& _rEvents ) const { m_xManager->registerScriptEvents( m_nIndex, _rEvents ); } const Reference< XInterface >& getComponent() const { return m_xComponent; } private: const Reference< XInterface > m_xComponent; const Reference< XEventAttacherManager > m_xManager; const sal_Int32 m_nIndex; }; //==================================================================== //= FormComponentIterator //==================================================================== class FormComponentIterator { public: FormComponentIterator( const Reference< XIndexAccess >& _rxContainer ) :m_xContainer( _rxContainer, UNO_SET_THROW ) ,m_xEventManager( _rxContainer, UNO_QUERY_THROW ) ,m_nElementCount( _rxContainer->getCount() ) ,m_nCurrentElement( 0 ) { } bool hasMore() const { return m_nCurrentElement < m_nElementCount; } FormComponentScripts next() { FormComponentScripts aComponent( Reference< XInterface >( m_xContainer->getByIndex( m_nCurrentElement ), UNO_QUERY_THROW ), m_xEventManager, m_nCurrentElement ); ++m_nCurrentElement; return aComponent; } private: const Reference< XIndexAccess > m_xContainer; const Reference< XEventAttacherManager > m_xEventManager; const sal_Int32 m_nElementCount; sal_Int32 m_nCurrentElement; }; //==================================================================== //= ScriptsStorage - declaration //==================================================================== /** a helper class which encapsulates access to the storages for Java/Script, BeanShell, and Python scripts, i.e. all script types which can be manipulated on storage level. */ class ScriptsStorage { public: ScriptsStorage( MigrationLog& _rLogger ); ScriptsStorage( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger ); ~ScriptsStorage(); /** determines whether the instance is valid, i.e. refers to a valid root storage for reading/storing scripts */ inline bool isValid() const { return m_xScriptsStorage.is(); } /** binds the instance to a new document. Only to be called when the instance is not yet bound (i.e. isValid returns ). */ void bind( const Reference< XModel >& _rxDocument ); /// determines whether scripts of the given type are present bool hasScripts( const ScriptType _eType ) const; /// returns the root storage for the scripts of the given type SharedStorage getScriptsRoot( const ScriptType _eType ) const; /** returns the names of the elements in the "Scripts" storage */ ::std::set< ::rtl::OUString > getElementNames() const; /** removes the sub storage for a given script type @precond the respective storage is empty @precond the ScriptsStorage instance was opened for writing */ void removeScriptTypeStorage( const ScriptType _eType ) const; /** commits the changes at our XStorage object */ bool commit(); /** removes the "Scripts" sub storage from the given document's root storage @precond the "Scripts" storage is empty */ static bool removeFromDocument( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger ); private: MigrationLog& m_rLogger; SharedStorage m_xScriptsStorage; }; //==================================================================== //= ScriptsStorage - implementation //==================================================================== //-------------------------------------------------------------------- ScriptsStorage::ScriptsStorage( MigrationLog& _rLogger ) :m_rLogger( _rLogger ) ,m_xScriptsStorage() { } //-------------------------------------------------------------------- ScriptsStorage::ScriptsStorage( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger ) :m_rLogger( _rLogger ) ,m_xScriptsStorage() { bind( _rxDocument ); } //-------------------------------------------------------------------- ScriptsStorage::~ScriptsStorage() { } //-------------------------------------------------------------------- bool ScriptsStorage::commit() { return lcl_commitStorage_nothrow( m_xScriptsStorage ); } //-------------------------------------------------------------------- void ScriptsStorage::bind( const Reference< XModel >& _rxDocument ) { OSL_PRECOND( !isValid(), "ScriptsStorage:bind: did not bother, yet, to check whether this is allowed!" ); try { Reference< XStorageBasedDocument > xStorageDoc( _rxDocument, UNO_QUERY_THROW ); Reference< XStorage > xDocStorage( xStorageDoc->getDocumentStorage(), UNO_QUERY_THROW ); // the "Scripts" storage exist, or if it does not (yet) exist and we are in write mode // => open the storage if ( ( xDocStorage->hasByName( lcl_getScriptsStorageName() ) && xDocStorage->isStorageElement( lcl_getScriptsStorageName() ) ) || !xDocStorage->hasByName( lcl_getScriptsStorageName() ) ) { m_xScriptsStorage.set( xDocStorage->openStorageElement( lcl_getScriptsStorageName(), ElementModes::READWRITE ), UNO_QUERY_THROW ); } } catch( const Exception& ) { m_rLogger.logFailure( MigrationError( ERR_BIND_SCRIPT_STORAGE_FAILED, ::comphelper::DocumentInfo::getDocumentTitle( _rxDocument ), ::cppu::getCaughtException() ) ); } } //-------------------------------------------------------------------- bool ScriptsStorage::hasScripts( const ScriptType _eType ) const { OSL_PRECOND( isValid(), "ScriptsStorage::hasScripts: illegal call!" ); if ( !isValid() ) return false; const ::rtl::OUString& rSubStorageName( lcl_getScriptsSubStorageName( _eType ) ); return m_xScriptsStorage->hasByName( rSubStorageName ) && m_xScriptsStorage->isStorageElement( rSubStorageName ); } //-------------------------------------------------------------------- SharedStorage ScriptsStorage::getScriptsRoot( const ScriptType _eType ) const { SharedStorage xStorage; if ( isValid() ) { xStorage.reset( m_xScriptsStorage->openStorageElement( lcl_getScriptsSubStorageName( _eType ), ElementModes::READWRITE ) ); } return xStorage; } //-------------------------------------------------------------------- ::std::set< ::rtl::OUString > ScriptsStorage::getElementNames() const { Sequence< ::rtl::OUString > aElementNames; if ( isValid() ) aElementNames = m_xScriptsStorage->getElementNames(); ::std::set< ::rtl::OUString > aNames; ::std::copy( aElementNames.getConstArray(), aElementNames.getConstArray() + aElementNames.getLength(), ::std::insert_iterator< ::std::set< ::rtl::OUString > >( aNames, aNames.end() ) ); return aNames; } //-------------------------------------------------------------------- void ScriptsStorage::removeScriptTypeStorage( const ScriptType _eType ) const { ::rtl::OUString sSubStorageName( lcl_getScriptsSubStorageName( _eType ) ); if ( m_xScriptsStorage->hasByName( sSubStorageName ) ) m_xScriptsStorage->removeElement( sSubStorageName ); } //-------------------------------------------------------------------- bool ScriptsStorage::removeFromDocument( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger ) { try { Reference< XStorageBasedDocument > xStorageDoc( _rxDocument, UNO_QUERY_THROW ); Reference< XStorage > xDocStorage( xStorageDoc->getDocumentStorage(), UNO_QUERY_THROW ); xDocStorage->removeElement( lcl_getScriptsStorageName() ); } catch( const Exception& ) { _rLogger.logFailure( MigrationError( ERR_REMOVE_SCRIPTS_STORAGE_FAILED, ::comphelper::DocumentInfo::getDocumentTitle( _rxDocument ), ::cppu::getCaughtException() ) ) ; return false; } return true; } //==================================================================== //= ProgressDelegator //==================================================================== class ProgressDelegator : public IProgressConsumer { public: ProgressDelegator( IMigrationProgress& _rDelegator, const ::rtl::OUString& _rObjectName, const ::rtl::OUString& _rAction ) :m_rDelegator( _rDelegator ) ,m_sObjectName( _rObjectName ) ,m_sAction( _rAction ) { } virtual ~ProgressDelegator() { } // IProgressConsumer virtual void start( sal_uInt32 _nRange ) { m_rDelegator.startObject( m_sObjectName, m_sAction, _nRange ); } virtual void advance( sal_uInt32 _nValue ) { m_rDelegator.setObjectProgressValue( _nValue ); } virtual void end() { m_rDelegator.endObject(); } private: IMigrationProgress& m_rDelegator; ::rtl::OUString m_sObjectName; ::rtl::OUString m_sAction; }; //==================================================================== //= PhaseGuard //==================================================================== class PhaseGuard { public: PhaseGuard( ProgressMixer& _rMixer ) :m_rMixer( _rMixer ) { } PhaseGuard( ProgressMixer& _rMixer, const PhaseID _nID, const sal_uInt32 _nPhaseRange ) :m_rMixer( _rMixer ) { start( _nID, _nPhaseRange ); } ~PhaseGuard() { m_rMixer.endPhase(); } void start( const PhaseID _nID, const sal_uInt32 _nPhaseRange ) { m_rMixer.startPhase( _nID, _nPhaseRange ); } private: ProgressMixer& m_rMixer; }; //==================================================================== //= MigrationEngine_Impl - declaration //==================================================================== class MigrationEngine_Impl { public: MigrationEngine_Impl( const ::comphelper::ComponentContext& _rContext, const Reference< XOfficeDatabaseDocument >& _rxDocument, IMigrationProgress& _rProgress, MigrationLog& _rLogger ); ~MigrationEngine_Impl(); inline size_t getFormCount() const { return m_nFormCount; } inline size_t getReportCount()const { return m_nReportCount; } bool migrateAll(); private: ::comphelper::ComponentContext m_aContext; const Reference< XOfficeDatabaseDocument > m_xDocument; const Reference< XModel > m_xDocumentModel; IMigrationProgress& m_rProgress; MigrationLog& m_rLogger; mutable DocumentID m_nCurrentDocumentID; SubDocuments m_aSubDocs; size_t m_nFormCount; size_t m_nReportCount; private: /** collects a description of all sub documents of our database document @return if and only if collecting the documents was successful */ bool impl_collectSubDocuments_nothrow(); /** migrates the macros/scripts of the given sub document */ bool impl_handleDocument_nothrow( const SubDocument& _rDocument ) const; /** checks the structure of the 'Scripts' folder of a sub document for unknown elements @return if and only if the 'Scripts' folder contains known elements only. */ bool impl_checkScriptStorageStructure_nothrow( const SubDocument& _rDocument ) const; /** migrates the scripts of the given "storage-based" script type */ bool impl_migrateScriptStorage_nothrow( const SubDocument& _rDocument, const ScriptType _eScriptType, ProgressMixer& _rProgress, const PhaseID _nPhaseID ) const; /** migrates the content of the given "container based" libraries (Basic/Dialogs) */ bool impl_migrateContainerLibraries_nothrow( const SubDocument& _rDocument, const ScriptType _eScriptType, ProgressMixer& _rProgress, const PhaseID _nPhaseID ) const; /** adjusts the events for the given dialog/element, taking into account the new names of the moved libraries */ void impl_adjustDialogElementEvents_throw( const Reference< XInterface >& _rxElement ) const; /** adjusts the events in the given dialog, and its controls, taking into account the new names of the moved libraries */ bool impl_adjustDialogEvents_nothrow( Any& _inout_rDialogLibraryElement, const ::rtl::OUString& _rDocName, const ::rtl::OUString& _rDialogLibName, const ::rtl::OUString& _rDialogName ) const; /** adjust the document-events which refer to macros/scripts in the document, taking into account the new names of the moved libraries */ bool impl_adjustDocumentEvents_nothrow( const SubDocument& _rDocument ) const; /** adjusts the script references bound to form component events */ bool impl_adjustFormComponentEvents_nothrow( const SubDocument& _rDocument ) const; /** adjusts the script references for the elements of the given form component container */ void impl_adjustFormComponentEvents_throw( const Reference< XIndexAccess >& _rxComponentContainer ) const; /** adjusts the library name in the given script URL, so that it reflects the new name of the library @return if and only if adjustments to the script code have been made */ bool impl_adjustScriptLibrary_nothrow( const ::rtl::OUString& _rScriptType, ::rtl::OUString& _inout_rScriptCode ) const; bool impl_adjustScriptLibrary_nothrow( Any& _inout_rScriptDescriptor ) const; bool impl_adjustScriptLibrary_nothrow( ScriptEventDescriptor& _inout_rScriptEvent ) const; /** asks the user for a password for the given library, and unprotects the library @return if and only if the library could be successfully unprotected */ bool impl_unprotectPasswordLibrary_throw( const Reference< XLibraryContainerPassword >& _rxPasswordManager, const ScriptType _eScriptType, const ::rtl::OUString& _rLibraryName ) const; }; //==================================================================== //= MigrationEngine_Impl - implementation //==================================================================== //-------------------------------------------------------------------- MigrationEngine_Impl::MigrationEngine_Impl( const ::comphelper::ComponentContext& _rContext, const Reference< XOfficeDatabaseDocument >& _rxDocument, IMigrationProgress& _rProgress, MigrationLog& _rLogger ) :m_aContext( _rContext ) ,m_xDocument( _rxDocument ) ,m_xDocumentModel( _rxDocument, UNO_QUERY_THROW ) ,m_rProgress( _rProgress ) ,m_rLogger( _rLogger ) ,m_nCurrentDocumentID( - 1 ) ,m_aSubDocs() ,m_nFormCount( 0 ) ,m_nReportCount( 0 ) { OSL_VERIFY( impl_collectSubDocuments_nothrow() ); } //-------------------------------------------------------------------- MigrationEngine_Impl::~MigrationEngine_Impl() { } //-------------------------------------------------------------------- bool MigrationEngine_Impl::migrateAll() { if ( m_aSubDocs.empty() ) { OSL_ENSURE( false, "MigrationEngine_Impl::migrateAll: no forms/reports found!" ); // The whole migration wizard is not expected to be called when there are no forms/reports // with macros, not to mention when there are no forms/reports at all. return false; } // initialize global progress sal_Int32 nOverallRange( m_aSubDocs.size() ); String sProgressSkeleton = String( MacroMigrationResId( STR_OVERALL_PROGRESS ) ); sProgressSkeleton.SearchAndReplaceAscii( "$overall$", String::CreateFromInt32( nOverallRange ) ); m_rProgress.start( nOverallRange ); for ( SubDocuments::const_iterator doc = m_aSubDocs.begin(); doc != m_aSubDocs.end(); ++doc ) { sal_Int32 nOverallProgressValue( doc - m_aSubDocs.begin() + 1 ); // update overall progress text ::rtl::OUString sOverallProgress( sProgressSkeleton ); ::comphelper::string::searchAndReplaceAsciiI( sOverallProgress, "$current$", ::rtl::OUString::valueOf( nOverallProgressValue ) ); m_rProgress.setOverallProgressText( sOverallProgress ); // migrate document if ( !impl_handleDocument_nothrow( *doc ) ) return false; // update overall progress vallue m_rProgress.setOverallProgressValue( nOverallProgressValue ); } // commit the root storage of the database document, for all changes made so far to take effect if ( !lcl_commitDocumentStorage_nothrow( m_xDocumentModel, m_rLogger ) ) return false; // save the document if ( !lcl_storeDocument_nothrow( m_xDocumentModel, m_rLogger ) ) return false; return true; } //-------------------------------------------------------------------- namespace { void lcl_collectHierarchicalElementNames_throw( const Reference< XNameAccess >& _rxContainer, const ::rtl::OUString& _rContainerLoc, SubDocuments& _out_rDocs, const SubDocumentType _eType, size_t& _io_counter ) { const ::rtl::OUString sHierarhicalBase( _rContainerLoc.getLength() ? ::rtl::OUStringBuffer( _rContainerLoc ).appendAscii( "/" ).makeStringAndClear() : ::rtl::OUString() ); Sequence< ::rtl::OUString > aElementNames( _rxContainer->getElementNames() ); for ( const ::rtl::OUString* elementName = aElementNames.getConstArray(); elementName != aElementNames.getConstArray() + aElementNames.getLength(); ++elementName ) { Any aElement( _rxContainer->getByName( *elementName ) ); ::rtl::OUString sElementName( ::rtl::OUStringBuffer( sHierarhicalBase ).append( *elementName ) ); Reference< XNameAccess > xSubContainer( aElement, UNO_QUERY ); if ( xSubContainer.is() ) { lcl_collectHierarchicalElementNames_throw( xSubContainer, sElementName, _out_rDocs, _eType, _io_counter ); } else { Reference< XCommandProcessor > xCommandProcessor( aElement, UNO_QUERY ); OSL_ENSURE( xCommandProcessor.is(), "lcl_collectHierarchicalElementNames_throw: no container, and no comand processor? What *is* it, then?!" ); if ( xCommandProcessor.is() ) { _out_rDocs.push_back( SubDocument( xCommandProcessor, sElementName, _eType, ++_io_counter ) ); } } } } } //-------------------------------------------------------------------- bool MigrationEngine_Impl::impl_collectSubDocuments_nothrow() { OSL_PRECOND( m_xDocument.is(), "MigrationEngine_Impl::impl_collectSubDocuments_nothrow: invalid document!" ); if ( !m_xDocument.is() ) return false; try { Reference< XNameAccess > xDocContainer( m_xDocument->getFormDocuments(), UNO_SET_THROW ); m_nFormCount = 0; lcl_collectHierarchicalElementNames_throw( xDocContainer, ::rtl::OUString(), m_aSubDocs, eForm, m_nFormCount ); xDocContainer.set( m_xDocument->getReportDocuments(), UNO_SET_THROW ); m_nReportCount = 0; lcl_collectHierarchicalElementNames_throw( xDocContainer, ::rtl::OUString(), m_aSubDocs, eReport, m_nReportCount ); } catch( const Exception& ) { m_rLogger.logFailure( MigrationError( ERR_COLLECTING_DOCUMENTS_FAILED, ::cppu::getCaughtException() ) ); return false; } return true; } //-------------------------------------------------------------------- bool MigrationEngine_Impl::impl_handleDocument_nothrow( const SubDocument& _rDocument ) const { OSL_ENSURE( m_nCurrentDocumentID == -1, "MigrationEngine_Impl::impl_handleDocument_nothrow: there already is a current document!"); m_nCurrentDocumentID = m_rLogger.startedDocument( _rDocument.eType, _rDocument.sHierarchicalName ); // start the progress ::rtl::OUString sObjectName( lcl_getSubDocumentDescription( _rDocument ) ); m_rProgress.startObject( sObjectName, ::rtl::OUString(), DEFAULT_DOC_PROGRESS_RANGE ); // ----------------- // load the document ::rtl::Reference< ProgressCapture > pStatusIndicator( new ProgressCapture( sObjectName, m_rProgress ) ); SubDocument aSubDocument( _rDocument ); OpenDocResult eResult = lcl_loadSubDocument_nothrow( aSubDocument, pStatusIndicator.get(), m_rLogger ); if ( eResult != eOpenedDoc ) { pStatusIndicator->dispose(); m_rProgress.endObject(); m_rLogger.finishedDocument( m_nCurrentDocumentID ); m_nCurrentDocumentID = -1; return ( eResult == eIgnoreDoc ); } // ----------------- // migrate the libraries ProgressDelegator aDelegator( m_rProgress, sObjectName, String( MacroMigrationResId( STR_MIGRATING_LIBS ) ) ); ProgressMixer aProgressMixer( aDelegator ); aProgressMixer.registerPhase( PHASE_JAVASCRIPT, 1 ); aProgressMixer.registerPhase( PHASE_BEANSHELL, 1 ); aProgressMixer.registerPhase( PHASE_PYTHON, 1 ); aProgressMixer.registerPhase( PHASE_JAVA, 1 ); aProgressMixer.registerPhase( PHASE_BASIC, 5 ); // more weight than then others, assuming that usually, there are much more Basic macros than any other scripts aProgressMixer.registerPhase( PHASE_DIALOGS, 1 ); bool bSuccess = impl_checkScriptStorageStructure_nothrow( aSubDocument ); // migrate storage-based script libraries (which can be handled by mere storage operations) bSuccess = bSuccess && impl_migrateScriptStorage_nothrow( aSubDocument, eJavaScript, aProgressMixer, PHASE_JAVASCRIPT ) && impl_migrateScriptStorage_nothrow( aSubDocument, eBeanShell, aProgressMixer, PHASE_BEANSHELL ) && impl_migrateScriptStorage_nothrow( aSubDocument, ePython, aProgressMixer, PHASE_PYTHON ) && impl_migrateScriptStorage_nothrow( aSubDocument, eJava, aProgressMixer, PHASE_JAVA ); // migrate Basic and dialog libraries bSuccess = bSuccess && impl_migrateContainerLibraries_nothrow( aSubDocument, eBasic, aProgressMixer, PHASE_BASIC ) && impl_migrateContainerLibraries_nothrow( aSubDocument, eDialog, aProgressMixer, PHASE_DIALOGS ); // order matters: First Basic scripts, then dialogs. So we can adjust references from the latter // to the former // adjust the events in the document // (note that errors are ignored here - failure to convert a script reference // is not considered a critical error) if ( bSuccess ) { impl_adjustDocumentEvents_nothrow( aSubDocument ); impl_adjustFormComponentEvents_nothrow( aSubDocument ); } // ----------------- // clean up // store the sub document, including removal of the (now obsolete) "Scripts" sub folder if ( m_rLogger.movedAnyLibrary( m_nCurrentDocumentID ) ) { bSuccess = bSuccess && ScriptsStorage::removeFromDocument( aSubDocument.xDocument, m_rLogger ) && lcl_commitDocumentStorage_nothrow( aSubDocument.xDocument, m_rLogger ) && lcl_storeEmbeddedDocument_nothrow( aSubDocument ); } // unload in any case, even if we were not successful bSuccess = lcl_unloadSubDocument_nothrow( aSubDocument, m_rLogger ) && bSuccess; pStatusIndicator->dispose(); // end the progress, just in case the ProgressCapture didn't receive the XStatusIndicator::end event m_rProgress.endObject(); m_rLogger.finishedDocument( m_nCurrentDocumentID ); m_nCurrentDocumentID = -1; return bSuccess; } //-------------------------------------------------------------------- namespace { static ::rtl::OUString lcl_createTargetLibName( const SubDocument& _rDocument, const ::rtl::OUString& _rSourceLibName, const Reference< XNameAccess >& _rxTargetContainer ) { // The new library name is composed from the prefix, the base name, and the old library name. const ::rtl::OUString sPrefix( ::rtl::OUString::createFromAscii( _rDocument.eType == eForm ? "Form_" : "Report_" ) ); ::rtl::OUString sBaseName( _rDocument.sHierarchicalName.copy( _rDocument.sHierarchicalName.lastIndexOf( '/' ) + 1 ) ); // Normalize this name. In our current storage implementation (and script containers in a document // are finally mapped to sub storages of the document storage), not all characters are allowed. // The bug requesting to change this is #i95409#. // Unfortunately, the storage implementation does not complain if you use invalid characters/names, but instead // it silently accepts them, and produces garbage in the file (#i95408). // So, until especially the former is fixed, we need to strip the name from all invalid characters. // #i95865# / 2008-11-06 / frank.schoenheit@sun.com // The general idea is to replace invalid characters with '_'. However, since "valid" essentially means // ASCII only, this implies that for a lot of languages, we would simply replace everything with '_', // which of course is not desired. // So, we use a heuristics: If the name contains at most 3 invalid characters, and as many valid as invalid // characters, then we use the replacement. Otherwise, we just use a unambiguous number for the sub document. sal_Int32 nValid=0, nInvalid=0; const sal_Unicode* pBaseName = sBaseName.getStr(); const sal_Int32 nBaseNameLen = sBaseName.getLength(); for ( sal_Int32 i=0; i replace them ::rtl::OUStringBuffer aReplacement; aReplacement.ensureCapacity( nBaseNameLen ); aReplacement.append( sBaseName ); const sal_Unicode* pReplacement = aReplacement.getStr(); for ( sal_Int32 i=0; ihasByName( sTargetName ) ) return sTargetName; } // "too many" invalid characters, or the name composed with the base name was already used. // (The latter is valid, since there can be multiple sub documents with the same base name, // in different levels in the hierarchy.) // In this case, just use the umambiguous sub document number. ::rtl::OUStringBuffer aNewLibName; aNewLibName.append( sPrefix ); aNewLibName.append( ::rtl::OUString::valueOf( sal_Int64( _rDocument.nNumber ) ) ); aNewLibName.appendAscii( "_" ); aNewLibName.append( _rSourceLibName ); return aNewLibName.makeStringAndClear(); } } //-------------------------------------------------------------------- bool MigrationEngine_Impl::impl_checkScriptStorageStructure_nothrow( const SubDocument& _rDocument ) const { OSL_PRECOND( _rDocument.xDocument.is(), "MigrationEngine_Impl::impl_checkScriptStorageStructure_nothrow: invalid document!" ); if ( !_rDocument.xDocument.is() ) return false; try { // the root storage of the document whose scripts are to be migrated ScriptsStorage aDocStorage( _rDocument.xDocument, m_rLogger ); if ( !aDocStorage.isValid() ) { // no scripts at all, or no scripts of the given type return !m_rLogger.hadFailure(); } ::std::set< ::rtl::OUString > aElementNames( aDocStorage.getElementNames() ); ScriptType aKnownStorageBasedTypes[] = { eBeanShell, eJavaScript, ePython, eJava }; for ( size_t i=0; i aStorageElements( xScriptsRoot->getElementNames() ); aPhase.start( _nPhaseID, aStorageElements.getLength() ); for ( const ::rtl::OUString* element = aStorageElements.getConstArray(); element != aStorageElements.getConstArray() + aStorageElements.getLength(); ++element ) { bool bIsScriptLibrary = xScriptsRoot->isStorageElement( *element ); OSL_ENSURE( bIsScriptLibrary, "MigrationEngine_Impl::impl_migrateScriptStorage_nothrow: warning: unknown scripts storage structure!" ); // we cannot handle this. We would need to copy this stream to the respective scripts storage // of the database document, but we cannot guarantee that the name is not used, yet, and we cannot // simply rename the thing. if ( !bIsScriptLibrary ) { m_rLogger.logFailure( MigrationError( ERR_UNEXPECTED_LIBSTORAGE_ELEMENT, lcl_getSubDocumentDescription( _rDocument ), getScriptTypeDisplayName( _eScriptType ), *element ) ); return false; } // ensure we have access to the DBDoc's scripts storage if ( !aDatabaseScripts.isValid() ) { // not needed 'til now aDatabaseScripts.bind( m_xDocumentModel ); if ( aDatabaseScripts.isValid() ) xTargetStorage = aDatabaseScripts.getScriptsRoot( _eScriptType ); if ( !xTargetStorage.is() ) { m_rLogger.logFailure( MigrationError( ERR_CREATING_DBDOC_SCRIPT_STORAGE_FAILED, getScriptTypeDisplayName( _eScriptType ) ) ); return false; } } // move the library to the DBDoc's scripts library, under the new name ::rtl::OUString sNewLibName( lcl_createTargetLibName( _rDocument, *element, xTargetStorage.getTyped().get() ) ); xScriptsRoot->moveElementTo( *element, xTargetStorage, sNewLibName ); // log the fact that we moved the library m_rLogger.movedLibrary( m_nCurrentDocumentID, _eScriptType, *element, sNewLibName ); // progress _rProgress.advancePhase( element - aStorageElements.getConstArray() ); } // commit the storages, so the changes we made persist if ( !lcl_commitStorage_nothrow( xScriptsRoot ) || ( xTargetStorage.is() && !lcl_commitStorage_nothrow( xTargetStorage ) ) ) { m_rLogger.logFailure( MigrationError( ERR_COMMITTING_SCRIPT_STORAGES_FAILED, getScriptTypeDisplayName( _eScriptType ), lcl_getSubDocumentDescription( _rDocument ) ) ); return false; } // now that the concrete scripts storage does not have any elements anymore, // remove it xScriptsRoot.reset( NULL ); // need to reset the storage to be allowed to remove it aDocStorage.removeScriptTypeStorage( _eScriptType ); // done so far bSuccess = aDocStorage.commit() && aDatabaseScripts.commit(); } catch( const Exception& ) { aException = ::cppu::getCaughtException(); bSuccess = false; } // log the error, if any if ( !bSuccess ) { m_rLogger.logFailure( MigrationError( ERR_GENERAL_SCRIPT_MIGRATION_FAILURE, getScriptTypeDisplayName( _eScriptType ), lcl_getSubDocumentDescription( _rDocument ), aException ) ); } return bSuccess; } //-------------------------------------------------------------------- bool MigrationEngine_Impl::impl_migrateContainerLibraries_nothrow( const SubDocument& _rDocument, const ScriptType _eScriptType, ProgressMixer& _rProgress, const PhaseID _nPhaseID ) const { OSL_PRECOND( ( _eScriptType == eBasic ) || ( _eScriptType == eDialog ), "MigrationEngine_Impl::impl_migrateContainerLibraries_nothrow: illegal script type!" ); bool bSuccess = false; PhaseGuard aPhase( _rProgress ); Any aException; do // artificial loop for flow control only { try { // access library container of the sub document Reference< XEmbeddedScripts > xSubDocScripts( _rDocument.xDocument, UNO_QUERY ); if ( !xSubDocScripts.is() ) { // no script support in the sub document -> nothing to migrate // (though ... this is suspicious, at least ...) bSuccess = true; break; } Reference< XStorageBasedLibraryContainer > xSourceLibraries( _eScriptType == eBasic ? xSubDocScripts->getBasicLibraries() : xSubDocScripts->getDialogLibraries(), UNO_QUERY_THROW ); Reference< XLibraryContainerPassword > xSourcePasswords( xSourceLibraries, UNO_QUERY ); OSL_ENSURE( xSourcePasswords.is(), "MigrationEngine_Impl::impl_migrateContainerLibraries_nothrow: suspicious: no password management for the source libraries!" ); Sequence< ::rtl::OUString > aSourceLibNames( xSourceLibraries->getElementNames() ); aPhase.start( _nPhaseID, aSourceLibNames.getLength() ); if ( !xSourceLibraries->hasElements() ) { bSuccess = true; break; } // create library containers for the document - those will be the target for the migration Reference< XStorageBasedDocument > xStorageDoc( m_xDocument, UNO_QUERY_THROW ); Reference< XStorageBasedLibraryContainer > xTargetLibraries; if ( _eScriptType == eBasic ) { xTargetLibraries.set( DocumentScriptLibraryContainer::create( m_aContext.getUNOContext(), xStorageDoc ), UNO_QUERY_THROW ); } else { xTargetLibraries.set( DocumentDialogLibraryContainer::create( m_aContext.getUNOContext(), xStorageDoc ), UNO_QUERY_THROW ); } // copy all libs to the target, with potentially renaming them const ::rtl::OUString* pSourceLibBegin = aSourceLibNames.getConstArray(); const ::rtl::OUString* pSourceLibEnd = pSourceLibBegin + aSourceLibNames.getLength(); for ( const ::rtl::OUString* pSourceLibName = pSourceLibBegin; pSourceLibName != pSourceLibEnd; ++pSourceLibName ) { // if the library is password-protected, ask the user to unprotect it if ( xSourcePasswords.is() && xSourcePasswords->isLibraryPasswordProtected( *pSourceLibName ) && !xSourcePasswords->isLibraryPasswordVerified( *pSourceLibName ) ) { if ( !impl_unprotectPasswordLibrary_throw( xSourcePasswords, _eScriptType, *pSourceLibName ) ) { m_rLogger.logFailure( MigrationError( ERR_PASSWORD_VERIFICATION_FAILED, _rDocument.sHierarchicalName, getScriptTypeDisplayName( _eScriptType ), *pSourceLibName ) ); return false; } } ::rtl::OUString sNewLibName( lcl_createTargetLibName( _rDocument, *pSourceLibName, xTargetLibraries.get() ) ); if ( xSourceLibraries->isLibraryLink( *pSourceLibName ) ) { // just re-create the link in the target library xTargetLibraries->createLibraryLink( sNewLibName, xSourceLibraries->getLibraryLinkURL( *pSourceLibName ), xSourceLibraries->isLibraryReadOnly( *pSourceLibName ) ); } else { if ( !xSourceLibraries->isLibraryLoaded( *pSourceLibName ) ) xSourceLibraries->loadLibrary( *pSourceLibName ); // copy the content of this particular library Reference< XNameAccess > xSourceLib( xSourceLibraries->getByName( *pSourceLibName ), UNO_QUERY_THROW ); Reference< XNameContainer > xTargetLib( xTargetLibraries->createLibrary( sNewLibName ), UNO_QUERY_THROW ); Sequence< ::rtl::OUString > aLibElementNames( xSourceLib->getElementNames() ); for ( const ::rtl::OUString* pSourceElementName = aLibElementNames.getConstArray(); pSourceElementName != aLibElementNames.getConstArray() + aLibElementNames.getLength(); ++pSourceElementName ) { Any aElement = xSourceLib->getByName( *pSourceElementName ); OSL_ENSURE( aElement.hasValue(), "MigrationEngine_Impl::impl_migrateContainerLibraries_nothrow: invalid (empty) lib element!" ); // if this is a dialog, adjust the references to scripts if ( _eScriptType == eDialog ) { impl_adjustDialogEvents_nothrow( aElement, lcl_getSubDocumentDescription( _rDocument ), *pSourceLibName, *pSourceElementName ); } xTargetLib->insertByName( *pSourceElementName, aElement ); } // transfer the read-only flag xTargetLibraries->setLibraryReadOnly( sNewLibName, xSourceLibraries->isLibraryReadOnly( *pSourceLibName ) ); } // remove the source lib xSourceLibraries->removeLibrary( *pSourceLibName ); // tell the logger m_rLogger.movedLibrary( m_nCurrentDocumentID, _eScriptType, *pSourceLibName, sNewLibName ); // tell the progress _rProgress.advancePhase( pSourceLibName - pSourceLibBegin ); } // clean up xSourceLibraries->storeLibraries(); xTargetLibraries->storeLibraries(); Reference< XStorage > xTargetRoot( xTargetLibraries->getRootLocation(), UNO_QUERY_THROW ); bSuccess = lcl_commitStorage_nothrow( xTargetRoot ); } catch( const Exception& ) { aException = ::cppu::getCaughtException(); bSuccess = false; } } while ( false ); // log the error, if any if ( !bSuccess ) { m_rLogger.logFailure( MigrationError( ERR_GENERAL_MACRO_MIGRATION_FAILURE, lcl_getSubDocumentDescription( _rDocument ), aException ) ); } return bSuccess; } //-------------------------------------------------------------------- bool MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow( const ::rtl::OUString& _rScriptType, ::rtl::OUString& _inout_rScriptCode ) const { OSL_PRECOND( _inout_rScriptCode.getLength(), "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: invalid script!" ); if ( !_inout_rScriptCode.getLength() ) return false; bool bSuccess = false; Any aException; try { if ( !_rScriptType.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Script" ) ) || !_rScriptType.getLength() ) { OSL_ENSURE( false, "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: no or unknown script type!" ); m_rLogger.logRecoverable( MigrationError( ERR_UNKNOWN_SCRIPT_TYPE, _rScriptType ) ); return false; } // analyze the script URI Reference< XUriReferenceFactory > xUriRefFac = UriReferenceFactory::create( m_aContext.getUNOContext() ); Reference< XVndSunStarScriptUrlReference > xUri( xUriRefFac->parse( _inout_rScriptCode ), UNO_QUERY_THROW ); ::rtl::OUString sScriptLanguage = xUri->getParameter( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "language" ) ) ); ScriptType eScriptType = eBasic; if ( !lcl_getScriptTypeFromLanguage( sScriptLanguage, eScriptType ) ) { OSL_ENSURE( false, "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: unknown script language!" ); m_rLogger.logRecoverable( MigrationError( ERR_UNKNOWN_SCRIPT_LANGUAGE, sScriptLanguage ) ); return false; } ::rtl::OUString sLocation = xUri->getParameter( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "location" ) ) ); if ( !sLocation.equalsAscii( "document" ) ) { // only document libraries must be migrated, of course return false; } ::rtl::OUString sScriptName = xUri->getName(); sal_Int32 nLibModuleSeparator = sScriptName.indexOf( '.' ); if ( nLibModuleSeparator < 0 ) { OSL_ENSURE( false, "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: invalid/unknown location format!" ); m_rLogger.logRecoverable( MigrationError( ERR_UNKNOWN_SCRIPT_NAME_FORMAT, sScriptName ) ); return false; } // replace the library name ::rtl::OUString sLibrary = sScriptName.copy( 0, nLibModuleSeparator ); ::rtl::OUString sNewLibName = m_rLogger.getNewLibraryName( m_nCurrentDocumentID, eScriptType, sLibrary ); OSL_ENSURE( sLibrary != sNewLibName, "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: a library which has not been migrated?" ); ::rtl::OUStringBuffer aNewLocation; aNewLocation.append( sNewLibName ); aNewLocation.append( sScriptName.copy( nLibModuleSeparator ) ); xUri->setName( aNewLocation.makeStringAndClear() ); // update the new script URL _inout_rScriptCode = xUri->getUriReference(); bSuccess = true; } catch( const Exception& ) { aException = ::cppu::getCaughtException(); bSuccess = false; } // log the failure, if any if ( !bSuccess ) { m_rLogger.logRecoverable( MigrationError( ERR_SCRIPT_TRANSLATION_FAILURE, _rScriptType, _inout_rScriptCode, aException ) ); } return bSuccess; } //-------------------------------------------------------------------- bool MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow( ScriptEventDescriptor& _inout_rScriptEvent ) const { if ( _inout_rScriptEvent.ScriptType.getLength() && _inout_rScriptEvent.ScriptCode.getLength() ) return impl_adjustScriptLibrary_nothrow( _inout_rScriptEvent.ScriptType, _inout_rScriptEvent.ScriptCode ); return false; } //-------------------------------------------------------------------- bool MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow( Any& _inout_rScriptDescriptor ) const { ::comphelper::NamedValueCollection aScriptDesc( _inout_rScriptDescriptor ); ::rtl::OUString sScriptType; ::rtl::OUString sScript; try { OSL_VERIFY( aScriptDesc.get_ensureType( "EventType", sScriptType ) ); OSL_VERIFY( aScriptDesc.get_ensureType( "Script", sScript ) ); } catch( const Exception& ) { m_rLogger.logRecoverable( MigrationError( ERR_INVALID_SCRIPT_DESCRIPTOR_FORMAT, ::cppu::getCaughtException() ) ); } if ( sScriptType.getLength() && sScript.getLength() ) if ( !impl_adjustScriptLibrary_nothrow( sScriptType, sScript ) ) return false; aScriptDesc.put( "Script", sScript ); _inout_rScriptDescriptor <<= aScriptDesc.getPropertyValues(); return true; } //-------------------------------------------------------------------- bool MigrationEngine_Impl::impl_adjustDocumentEvents_nothrow( const SubDocument& _rDocument ) const { try { Reference< XEventsSupplier > xSuppEvents( _rDocument.xDocument, UNO_QUERY ); if ( !xSuppEvents.is() ) // this is allowed. E.g. new-style reports currently do not support this return true; Reference< XNameReplace > xEvents( xSuppEvents->getEvents(), UNO_SET_THROW ); Sequence< ::rtl::OUString > aEventNames = xEvents->getElementNames(); Any aEvent; for ( const ::rtl::OUString* eventName = aEventNames.getConstArray(); eventName != aEventNames.getConstArray() + aEventNames.getLength(); ++eventName ) { aEvent = xEvents->getByName( *eventName ); if ( !aEvent.hasValue() ) continue; // translate if ( !impl_adjustScriptLibrary_nothrow( aEvent ) ) continue; // put back xEvents->replaceByName( *eventName, aEvent ); } } catch( const Exception& ) { m_rLogger.logRecoverable( MigrationError( ERR_ADJUSTING_DOCUMENT_EVENTS_FAILED, lcl_getSubDocumentDescription( _rDocument ), ::cppu::getCaughtException() ) ); return false; } return true; } //-------------------------------------------------------------------- void MigrationEngine_Impl::impl_adjustDialogElementEvents_throw( const Reference< XInterface >& _rxElement ) const { Reference< XScriptEventsSupplier > xEventsSupplier( _rxElement, UNO_QUERY_THROW ); Reference< XNameReplace > xEvents( xEventsSupplier->getEvents(), UNO_QUERY_THROW ); Sequence< ::rtl::OUString > aEventNames( xEvents->getElementNames() ); const ::rtl::OUString* eventName = aEventNames.getArray(); const ::rtl::OUString* eventNamesEnd = eventName + aEventNames.getLength(); ScriptEventDescriptor aScriptEvent; for ( ; eventName != eventNamesEnd; ++eventName ) { OSL_VERIFY( xEvents->getByName( *eventName ) >>= aScriptEvent ); if ( !impl_adjustScriptLibrary_nothrow( aScriptEvent ) ) continue; xEvents->replaceByName( *eventName, makeAny( aScriptEvent ) ); } } //-------------------------------------------------------------------- bool MigrationEngine_Impl::impl_adjustDialogEvents_nothrow( Any& _inout_rDialogLibraryElement, const ::rtl::OUString& _rDocName, const ::rtl::OUString& _rDialogLibName, const ::rtl::OUString& _rDialogName ) const { try { // load a dialog model from the stream describing it Reference< XInputStreamProvider > xISP( _inout_rDialogLibraryElement, UNO_QUERY_THROW ); Reference< XInputStream > xInput( xISP->createInputStream(), UNO_QUERY_THROW ); Reference< XNameContainer > xDialogModel( m_aContext.createComponent( "com.sun.star.awt.UnoControlDialogModel" ), UNO_QUERY_THROW ); ::xmlscript::importDialogModel( xInput, xDialogModel, m_aContext.getUNOContext() ); // adjust the events of the dialog impl_adjustDialogElementEvents_throw( xDialogModel ); // adjust the events of the controls Sequence< ::rtl::OUString > aControlNames( xDialogModel->getElementNames() ); const ::rtl::OUString* controlName = aControlNames.getConstArray(); const ::rtl::OUString* controlNamesEnd = controlName + aControlNames.getLength(); for ( ; controlName != controlNamesEnd; ++controlName ) { impl_adjustDialogElementEvents_throw( Reference< XInterface >( xDialogModel->getByName( *controlName ), UNO_QUERY ) ); } // export dialog model xISP = ::xmlscript::exportDialogModel( xDialogModel, m_aContext.getUNOContext() ); _inout_rDialogLibraryElement <<= xISP; } catch( const Exception& ) { m_rLogger.logRecoverable( MigrationError( ERR_ADJUSTING_DIALOG_EVENTS_FAILED, _rDocName, _rDialogLibName, _rDialogName, ::cppu::getCaughtException() ) ); return false; } return true; } //-------------------------------------------------------------------- void MigrationEngine_Impl::impl_adjustFormComponentEvents_throw( const Reference< XIndexAccess >& _rxComponentContainer ) const { FormComponentIterator aCompIter( _rxComponentContainer ); while ( aCompIter.hasMore() ) { // 1. adjust the component's scripts of the current component FormComponentScripts aComponent( aCompIter.next() ); Sequence< ScriptEventDescriptor > aEvents( aComponent.getEvents() ); bool bChangedComponentEvents = false; for ( ScriptEventDescriptor* scriptEvent = aEvents.getArray(); scriptEvent != aEvents.getArray() + aEvents.getLength(); ++scriptEvent ) { if ( !impl_adjustScriptLibrary_nothrow( *scriptEvent ) ) continue; bChangedComponentEvents = true; } if ( bChangedComponentEvents ) aComponent.setEvents( aEvents ); // 2. step down if the component is a container itself Reference< XIndexAccess > xContainer( aComponent.getComponent(), UNO_QUERY ); if ( xContainer.is() ) impl_adjustFormComponentEvents_throw( xContainer ); } } //-------------------------------------------------------------------- bool MigrationEngine_Impl::impl_adjustFormComponentEvents_nothrow( const SubDocument& _rDocument ) const { try { DrawPageIterator aPageIter( _rDocument.xDocument ); while ( aPageIter.hasMore() ) { Reference< XFormsSupplier > xSuppForms( aPageIter.next(), UNO_QUERY_THROW ); Reference< XIndexAccess > xForms( xSuppForms->getForms(), UNO_QUERY_THROW ); impl_adjustFormComponentEvents_throw( xForms ); } } catch( const Exception& ) { m_rLogger.logRecoverable( MigrationError( ERR_ADJUSTING_FORMCOMP_EVENTS_FAILED, lcl_getSubDocumentDescription( _rDocument ), ::cppu::getCaughtException() ) ); return false; } return true; } //-------------------------------------------------------------------- bool MigrationEngine_Impl::impl_unprotectPasswordLibrary_throw( const Reference< XLibraryContainerPassword >& _rxPasswordManager, const ScriptType _eScriptType, const ::rtl::OUString& _rLibraryName ) const { // a human-readable description of the affected library ::rtl::OUString sLibraryDescription( String( MacroMigrationResId( STR_LIBRARY_TYPE_AND_NAME ) ) ); ::comphelper::string::searchAndReplaceAsciiI( sLibraryDescription, "$type$", getScriptTypeDisplayName( _eScriptType ) ); ::comphelper::string::searchAndReplaceAsciiI( sLibraryDescription, "$library$", _rLibraryName ); InteractionHandler aHandler( m_aContext, m_xDocumentModel ); ::rtl::OUString sPassword; while ( true ) { if ( !aHandler.requestDocumentPassword( sLibraryDescription, sPassword ) ) // aborted by the user return false; bool bSuccessVerification = _rxPasswordManager->verifyLibraryPassword( _rLibraryName, sPassword ); if ( bSuccessVerification ) return true; } } //==================================================================== //= MigrationEngine //==================================================================== //-------------------------------------------------------------------- MigrationEngine::MigrationEngine( const ::comphelper::ComponentContext& _rContext, const Reference< XOfficeDatabaseDocument >& _rxDocument, IMigrationProgress& _rProgress, MigrationLog& _rLogger ) :m_pImpl( new MigrationEngine_Impl( _rContext, _rxDocument, _rProgress, _rLogger ) ) { } //-------------------------------------------------------------------- MigrationEngine::~MigrationEngine() { } //-------------------------------------------------------------------- sal_Int32 MigrationEngine::getFormCount() const { return m_pImpl->getFormCount(); } //-------------------------------------------------------------------- sal_Int32 MigrationEngine::getReportCount() const { return m_pImpl->getReportCount(); } //-------------------------------------------------------------------- bool MigrationEngine::migrateAll() { return m_pImpl->migrateAll(); } //........................................................................ } // namespace dbmm //........................................................................