Files
libreoffice/dbaccess/source/ext/macromigration/migrationengine.cxx
Caolán McNamara 00657aef09 migrate to boost::gettext
* all .ui files go from <interface> to <interface domain="MODULE"> e.g. vcl
* all .src files go away and the english source strings folded into the .hrc as NC_("context", "source string")
* ResMgr is dropped in favour of std::locale imbued by boost::locale::generator pointed at matching
  MODULE .mo files
* UIConfig translations are folded into the module .mo, so e.g. UIConfig_cui
  goes from l10n target to normal one, so the res/lang.zips of UI files go away
* translation via Translation::get(hrc-define-key, imbued-std::locale)
* python can now be translated with its inbuilt gettext support (we keep the name strings.hrc there
  to keep finding the .hrc file uniform) so magic numbers can go away there
* java and starbasic components can be translated via the pre-existing css.resource.StringResourceWithLocation
  mechanism
* en-US res files go away, their strings are now the .hrc keys in the source code
* remaining .res files are replaced by .mo files
* in .res/.ui-lang-zip files, the old scheme missing translations of strings
  results in inserting the english original so something can be found, now the
  standard fallback of using the english original from the source key is used, so
  partial translations shrink dramatically in size
* extract .hrc strings with hrcex which backs onto
   xgettext -C --add-comments --keyword=NC_:1c,2 --from-code=UTF-8 --no-wrap
* extract .ui strings with uiex which backs onto
   xgettext --add-comments --no-wrap
* qtz for gettext translations is generated at runtime as ascii-ified crc32 of
   content + "|" + msgid
* [API CHANGE] remove deprecated binary .res resouce loader related uno apis
      com::sun::resource::OfficeResourceLoader
      com::sun::resource::XResourceBundleLoader
      com::sun::resource::XResourceBundle
    when translating strings via uno apis
      com.sun.star.resource.StringResourceWithLocation
    can continue to be used

Change-Id: Ia2594a2672b7301d9c3421fdf31b6cfe7f3f8d0a
2017-07-21 08:20:50 +01:00

1829 lines
71 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* 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 .
*/
#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"
#include "core_resource.hxx"
#include "strings.hrc"
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/ucb/XCommandProcessor.hpp>
#include <com/sun/star/ucb/XContent.hpp>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/document/XStorageBasedDocument.hpp>
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/frame/XStorable.hpp>
#include <com/sun/star/script/DocumentScriptLibraryContainer.hpp>
#include <com/sun/star/script/DocumentDialogLibraryContainer.hpp>
#include <com/sun/star/document/XEmbeddedScripts.hpp>
#include <com/sun/star/document/XEventsSupplier.hpp>
#include <com/sun/star/uri/UriReferenceFactory.hpp>
#include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp>
#include <com/sun/star/form/XFormsSupplier.hpp>
#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
#include <com/sun/star/script/XEventAttacherManager.hpp>
#include <com/sun/star/script/XLibraryContainerPassword.hpp>
#include <com/sun/star/io/WrongFormatException.hpp>
#include <com/sun/star/script/XScriptEventsSupplier.hpp>
#include <com/sun/star/io/XInputStreamProvider.hpp>
#include <comphelper/documentinfo.hxx>
#include <comphelper/interaction.hxx>
#include <comphelper/namedvaluecollection.hxx>
#include <comphelper/storagehelper.hxx>
#include <comphelper/types.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <tools/diagnose_ex.h>
#include <rtl/ustrbuf.hxx>
#include <rtl/ref.hxx>
#include <unotools/sharedunocomponent.hxx>
#include <xmlscript/xmldlg_imexp.hxx>
#include <vector>
#include <set>
#include <iterator>
#define DEFAULT_DOC_PROGRESS_RANGE 100000
namespace dbmm
{
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::uno::XComponentContext;
using ::com::sun::star::sdb::XOfficeDatabaseDocument;
using ::com::sun::star::container::XNameAccess;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::lang::XComponent;
using ::com::sun::star::frame::XModel;
using ::com::sun::star::ucb::XCommandProcessor;
using ::com::sun::star::ucb::XContent;
using ::com::sun::star::ucb::Command;
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::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;
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
OUString sHierarchicalName;
SubDocumentType eType;
size_t nNumber;
SubDocument( const Reference< XCommandProcessor >& _rxCommandProcessor, const 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 char sScriptsStorageName[] = "Scripts";
OUString lcl_getScriptsSubStorageName( const ScriptType _eType )
{
switch ( _eType )
{
case eBeanShell: return OUString("beanshell");
case eJavaScript: return OUString("javascript");
case ePython: return OUString("python"); // TODO: is this correct?
case eJava: return OUString("java");
default:
break;
}
OSL_FAIL( "lcl_getScriptsSubStorageName: illegal type!" );
return OUString();
}
bool lcl_getScriptTypeFromLanguage( const OUString& _rLanguage, ScriptType& _out_rScriptType )
{
struct LanguageMapping
{
const char* pAsciiLanguage;
const ScriptType eScriptType;
LanguageMapping( const char* _pAsciiLanguage, const ScriptType _eScriptType )
:pAsciiLanguage( _pAsciiLanguage )
,eScriptType( _eScriptType )
{
}
};
const LanguageMapping aLanguageMapping[] =
{
LanguageMapping( "JavaScript", eJavaScript ),
LanguageMapping( "BeanShell", eBeanShell ),
LanguageMapping( "Java", eJava ),
LanguageMapping( "Python", ePython ), // TODO: is this correct?
LanguageMapping( "Basic", eBasic )
};
for (const LanguageMapping& i : aLanguageMapping)
{
if ( _rLanguage.equalsAscii( i.pAsciiLanguage ) )
{
_out_rScriptType = i.eScriptType;
return true;
}
}
OSL_FAIL( "lcl_getScriptTypeFromLanguage: unknown language!" );
return false;
}
OUString lcl_getSubDocumentDescription( const SubDocument& _rDocument )
{
OUString sObjectName(
DBA_RES(
_rDocument.eType == eForm ? STR_FORM : STR_REPORT).
replaceFirst("$name$", _rDocument.sHierarchicalName));
return sObjectName;
}
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 = OUString::createFromAscii( _pAsciiCommand );
return _rxCommandProc->execute(
aCommand, _rxCommandProc->createCommandIdentifier(), nullptr );
}
OUString lcl_getMimeType_nothrow( const Reference< XCommandProcessor >& _rxContent )
{
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
};
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", true );
aLoadArgs.put( "StatusIndicator", _rxProgress );
Reference< XCommandProcessor > xCommandProcessor( _rDocument.xCommandProcessor, UNO_SET_THROW );
Command aCommand;
aCommand.Name = "openDesign";
aCommand.Argument <<= aLoadArgs.getPropertyValues();
Reference< XComponent > xDocComponent(
xCommandProcessor->execute(
aCommand, xCommandProcessor->createCommandIdentifier(), nullptr
),
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 ) == "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;
}
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:
explicit DrawPageIterator( const Reference< XModel >& _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:
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 )
,m_xManager( _rxManager )
,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:
explicit FormComponentIterator( const Reference< XIndexAccess >& _rxContainer )
:m_xContainer( _rxContainer )
,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:
explicit ScriptsStorage( MigrationLog& _rLogger );
ScriptsStorage( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger );
/** determines whether the instance is valid, i.e. refers to a valid root storage
for reading/storing scripts
*/
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 <FALSE/>).
*/
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< 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 );
}
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( sScriptsStorageName )
&& xDocStorage->isStorageElement( sScriptsStorageName )
)
|| !xDocStorage->hasByName( sScriptsStorageName )
)
{
m_xScriptsStorage.set(
xDocStorage->openStorageElement(
sScriptsStorageName, 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 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< OUString > ScriptsStorage::getElementNames() const
{
Sequence< OUString > aElementNames;
if ( isValid() )
aElementNames = m_xScriptsStorage->getElementNames();
std::set< OUString > aNames;
std::copy(
aElementNames.begin(),
aElementNames.end(),
std::insert_iterator< std::set< OUString > >( aNames, aNames.end() )
);
return aNames;
}
void ScriptsStorage::removeScriptTypeStorage( const ScriptType _eType ) const
{
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( sScriptsStorageName );
}
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 OUString& _rObjectName,
const OUString& _rAction
)
:m_rDelegator( _rDelegator )
,m_sObjectName( _rObjectName )
,m_sAction( _rAction )
{
}
virtual ~ProgressDelegator()
{
}
// IProgressConsumer
virtual void start( sal_uInt32 _nRange ) override
{
m_rDelegator.startObject( m_sObjectName, m_sAction, _nRange );
}
virtual void advance( sal_uInt32 _nValue ) override
{
m_rDelegator.setObjectProgressValue( _nValue );
}
virtual void end() override
{
m_rDelegator.endObject();
}
private:
IMigrationProgress& m_rDelegator;
OUString m_sObjectName;
OUString m_sAction;
};
// PhaseGuard
class PhaseGuard
{
public:
explicit PhaseGuard( ProgressMixer& _rMixer )
:m_rMixer( _rMixer )
{
}
~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 Reference<XComponentContext>& _rContext,
const Reference< XOfficeDatabaseDocument >& _rxDocument,
IMigrationProgress& _rProgress,
MigrationLog& _rLogger
);
size_t getFormCount() const { return m_nFormCount; }
size_t getReportCount()const { return m_nReportCount; }
bool migrateAll();
private:
Reference<XComponentContext> 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
<TRUE/> 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
<TRUE/> 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 OUString& _rDocName,
const OUString& _rDialogLibName,
const 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
*/
void impl_adjustDocumentEvents_nothrow(
const SubDocument& _rDocument
) const;
/** adjusts the script references bound to form component events
*/
void 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 <TRUE/>
if and only if adjustments to the script code have been made
*/
bool impl_adjustScriptLibrary_nothrow(
const OUString& _rScriptType,
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 <TRUE/>
if and only if the library could be successfully unprotected
*/
bool impl_unprotectPasswordLibrary_throw(
const Reference< XLibraryContainerPassword >& _rxPasswordManager,
const ScriptType _eScriptType,
const OUString& _rLibraryName
) const;
};
// MigrationEngine_Impl - implementation
MigrationEngine_Impl::MigrationEngine_Impl( const Reference<XComponentContext>& _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() );
}
bool MigrationEngine_Impl::migrateAll()
{
if ( m_aSubDocs.empty() )
{
OSL_FAIL( "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() );
OUString sProgressSkeleton(
DBA_RES( STR_OVERALL_PROGRESS).
replaceFirst("$overall$", OUString::number(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
OUString sOverallProgress(
sProgressSkeleton.replaceFirst("$current$",
OUString::number(nOverallProgressValue)));
m_rProgress.setOverallProgressText( sOverallProgress );
// migrate document
if ( !impl_handleDocument_nothrow( *doc ) )
return false;
// update overall progress value
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 OUString& _rContainerLoc,
SubDocuments& _out_rDocs, const SubDocumentType _eType, size_t& _io_counter )
{
const OUString sHierarhicalBase(
_rContainerLoc.isEmpty() ? OUString() :
OUString( _rContainerLoc + "/" ) );
Sequence< OUString > aElementNames( _rxContainer->getElementNames() );
for ( auto const & elementName : aElementNames )
{
Any aElement( _rxContainer->getByName( elementName ) );
OUString sElementName( sHierarhicalBase + 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 command 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, OUString(), m_aSubDocs, eForm, m_nFormCount );
xDocContainer.set( m_xDocument->getReportDocuments(), UNO_SET_THROW );
m_nReportCount = 0;
lcl_collectHierarchicalElementNames_throw( xDocContainer, 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
OUString sObjectName( lcl_getSubDocumentDescription( _rDocument ) );
m_rProgress.startObject( sObjectName, 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, DBA_RES(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 the others, assuming that usually, there are many 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
{
OUString lcl_createTargetLibName( const SubDocument& _rDocument,
const OUString& _rSourceLibName, const Reference< XNameAccess >& _rxTargetContainer )
{
// The new library name is composed from the prefix, the base name, and the old library name.
const OUString sPrefix = (_rDocument.eType == eForm)?OUString("Form_"): OUString("Report_");
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 all invalid characters from the name.
// #i95865#
// 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<nBaseNameLen; ++i )
{
if ( ::comphelper::OStorageHelper::IsValidZipEntryFileName( pBaseName + i, 1, false ) )
++nValid;
else
++nInvalid;
}
if ( ( nInvalid <= 3 ) && ( nInvalid * 2 <= nValid ) )
{ // not "too many" invalid => replace them
OUStringBuffer aReplacement;
aReplacement.ensureCapacity( nBaseNameLen );
aReplacement.append( sBaseName );
const sal_Unicode* pReplacement = aReplacement.getStr();
for ( sal_Int32 i=0; i<nBaseNameLen; ++i )
{
if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( pReplacement + i, 1, false ) )
aReplacement[i] = '_';
}
sBaseName = aReplacement.makeStringAndClear();
OUString sTargetName( sPrefix + sBaseName + "_" + _rSourceLibName );
if ( !_rxTargetContainer->hasByName( 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 unambiguous sub document number.
return sPrefix + OUString::number( _rDocument.nNumber ) + "_" + _rSourceLibName;
}
}
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< OUString > aElementNames( aDocStorage.getElementNames() );
const ScriptType aKnownStorageBasedTypes[] = {
eBeanShell, eJavaScript, ePython, eJava
};
for (ScriptType aKnownStorageBasedType : aKnownStorageBasedTypes)
aElementNames.erase( lcl_getScriptsSubStorageName( aKnownStorageBasedType ) );
if ( !aElementNames.empty() )
{
m_rLogger.logFailure( MigrationError(
ERR_UNKNOWN_SCRIPT_FOLDER,
lcl_getSubDocumentDescription( _rDocument ),
*aElementNames.begin()
) );
return false;
}
}
catch( const Exception& )
{
m_rLogger.logFailure( MigrationError(
ERR_EXAMINING_SCRIPTS_FOLDER_FAILED,
lcl_getSubDocumentDescription( _rDocument ),
::cppu::getCaughtException()
) );
return false;
}
return true;
}
bool MigrationEngine_Impl::impl_migrateScriptStorage_nothrow( const SubDocument& _rDocument,
const ScriptType _eScriptType, ProgressMixer& _rProgress, const PhaseID _nPhaseID ) const
{
OSL_PRECOND( _rDocument.xDocument.is(), "MigrationEngine_Impl::impl_migrateScriptStorage_nothrow: invalid document!" );
if ( !_rDocument.xDocument.is() )
return false;
ScriptsStorage aDatabaseScripts( m_rLogger );
// the scripts of our complete database document - created on demand only
SharedStorage xTargetStorage;
// the target for moving the scripts storages - created on demand only
PhaseGuard aPhase( _rProgress );
bool bSuccess = false;
Any aException;
try
{
// the root storage of the document whose scripts are to be migrated
ScriptsStorage aDocStorage( _rDocument.xDocument, m_rLogger );
if ( !aDocStorage.isValid()
|| !aDocStorage.hasScripts( _eScriptType )
)
{
// no scripts at all, or no scripts of the given type
_rProgress.startPhase( _nPhaseID, 1 );
_rProgress.endPhase();
return !m_rLogger.hadFailure();
}
SharedStorage xScriptsRoot( aDocStorage.getScriptsRoot( _eScriptType ) );
if ( !xScriptsRoot.is() )
throw RuntimeException("internal error");
// loop through the script libraries
Sequence< OUString > aStorageElements( xScriptsRoot->getElementNames() );
aPhase.start( _nPhaseID, aStorageElements.getLength() );
for ( const 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
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( nullptr ); // 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< 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, xStorageDoc ), UNO_QUERY_THROW );
}
else
{
xTargetLibraries.set( DocumentDialogLibraryContainer::create(
m_aContext, xStorageDoc ), UNO_QUERY_THROW );
}
// copy all libs to the target, with potentially renaming them
const OUString* pSourceLibBegin = aSourceLibNames.getConstArray();
const OUString* pSourceLibEnd = pSourceLibBegin + aSourceLibNames.getLength();
for ( const 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;
}
}
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< OUString > aLibElementNames( xSourceLib->getElementNames() );
for ( auto const & sourceElementName : aLibElementNames )
{
Any aElement = xSourceLib->getByName( sourceElementName );
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, sourceElementName );
}
xTargetLib->insertByName( sourceElementName, 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 OUString& _rScriptType,
OUString& _inout_rScriptCode ) const
{
OSL_PRECOND( !_inout_rScriptCode.isEmpty(), "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: invalid script!" );
if ( _inout_rScriptCode.isEmpty() )
return false;
bool bSuccess = false;
Any aException;
try
{
if ( _rScriptType != "Script" || _rScriptType.isEmpty() )
{
OSL_FAIL(
"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 );
Reference< XVndSunStarScriptUrlReference > xUri( xUriRefFac->parse( _inout_rScriptCode ), UNO_QUERY_THROW );
OUString sScriptLanguage = xUri->getParameter( "language" );
ScriptType eScriptType = eBasic;
if ( !lcl_getScriptTypeFromLanguage( sScriptLanguage, eScriptType ) )
{
OSL_FAIL(
"MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: unknown script language!" );
m_rLogger.logRecoverable( MigrationError(
ERR_UNKNOWN_SCRIPT_LANGUAGE,
sScriptLanguage
) );
return false;
}
OUString sLocation = xUri->getParameter( "location" );
if ( sLocation != "document" )
{
// only document libraries must be migrated, of course
return false;
}
OUString sScriptName = xUri->getName();
sal_Int32 nLibModuleSeparator = sScriptName.indexOf( '.' );
if ( nLibModuleSeparator < 0 )
{
OSL_FAIL(
"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
OUString sLibrary = sScriptName.copy( 0, nLibModuleSeparator );
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?" );
xUri->setName( sNewLibName + sScriptName.copy( nLibModuleSeparator ) );
// 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.isEmpty() || _inout_rScriptEvent.ScriptCode.isEmpty()) )
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 );
OUString sScriptType;
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.isEmpty() || sScript.isEmpty()) )
if ( !impl_adjustScriptLibrary_nothrow( sScriptType, sScript ) )
return false;
aScriptDesc.put( "Script", sScript );
_inout_rScriptDescriptor <<= aScriptDesc.getPropertyValues();
return true;
}
void 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;
Reference< XNameReplace > xEvents( xSuppEvents->getEvents(), UNO_SET_THROW );
Sequence< OUString > aEventNames = xEvents->getElementNames();
Any aEvent;
for ( auto const & eventName : aEventNames )
{
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()
) );
}
}
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< OUString > aEventNames( xEvents->getElementNames() );
ScriptEventDescriptor aScriptEvent;
for ( OUString const & eventName : aEventNames )
{
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 OUString& _rDocName, const OUString& _rDialogLibName, const 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->getServiceManager()->createInstanceWithContext("com.sun.star.awt.UnoControlDialogModel", m_aContext), UNO_QUERY_THROW );
::xmlscript::importDialogModel( xInput, xDialogModel, m_aContext, m_xDocumentModel );
// adjust the events of the dialog
impl_adjustDialogElementEvents_throw( xDialogModel );
// adjust the events of the controls
Sequence< OUString > aControlNames( xDialogModel->getElementNames() );
const OUString* controlName = aControlNames.getConstArray();
const 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, m_xDocumentModel );
_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 )
{
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 );
}
}
void 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()
) );
}
}
bool MigrationEngine_Impl::impl_unprotectPasswordLibrary_throw( const Reference< XLibraryContainerPassword >& _rxPasswordManager,
const ScriptType _eScriptType, const OUString& _rLibraryName ) const
{
// a human-readable description of the affected library
OUString sLibraryDescription(
DBA_RES(STR_LIBRARY_TYPE_AND_NAME).
replaceFirst("$type$",
getScriptTypeDisplayName(_eScriptType)).
replaceFirst("$library$", _rLibraryName));
//TODO: probably broken if first replaceFirst can produce
// fresh instance of "$library$" in subject string of second
// replaceFirst
InteractionHandler aHandler( m_aContext, m_xDocumentModel );
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 Reference<XComponentContext>& _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
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */