blob: 86b059cc01efe34176871c9861b54a9e1b4ab7c3 [file] [log] [blame]
/**************************************************************
*
* 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_dbaccess.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 <com/sun/star/sdb/XFormDocumentsSupplier.hpp>
#include <com/sun/star/sdb/XReportDocumentsSupplier.hpp>
#include <com/sun/star/util/XCloseable.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/frame/XComponentLoader.hpp>
#include <com/sun/star/ucb/XCommandProcessor.hpp>
#include <com/sun/star/ucb/XContent.hpp>
#include <com/sun/star/embed/XComponentSupplier.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/embed/XEmbedPersist.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>
/** === end UNO includes === **/
#include <comphelper/documentinfo.hxx>
#include <comphelper/interaction.hxx>
#include <comphelper/namedvaluecollection.hxx>
#include <comphelper/storagehelper.hxx>
#include <comphelper/string.hxx>
#include <comphelper/types.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <tools/string.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>
#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 <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< ::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 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
<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 ::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 <TRUE/>
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 <TRUE/>
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<nBaseNameLen; ++i )
{
if ( ::comphelper::OStorageHelper::IsValidZipEntryFileName( pBaseName + i, 1, sal_False ) )
++nValid;
else
++nInvalid;
}
if ( ( nInvalid <= 3 ) && ( nInvalid * 2 <= nValid ) )
{ // not "too many" invalid => replace them
::rtl::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, sal_False ) )
aReplacement.setCharAt( i, '_' );
}
sBaseName = aReplacement.makeStringAndClear();
::rtl::OUStringBuffer aNewLibNameAttempt;
aNewLibNameAttempt.append( sPrefix );
aNewLibNameAttempt.append( sBaseName );
aNewLibNameAttempt.appendAscii( "_" );
aNewLibNameAttempt.append( _rSourceLibName );
::rtl::OUString sTargetName( aNewLibNameAttempt.makeStringAndClear() );
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 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<sizeof( aKnownStorageBasedTypes ) / sizeof( aKnownStorageBasedTypes[0] ); ++i )
aElementNames.erase( lcl_getScriptsSubStorageName( aKnownStorageBasedTypes[i] ) );
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( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "internal error" ) ), NULL );
// loop through the script libraries
Sequence< ::rtl::OUString > 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 libary
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
//........................................................................