Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / dbaccess / source / ext / macromigration / migrationengine.cxx
blobe2c09925293ce44f6ee7a48456d3acba582ed68b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "dbmm_global.hrc"
21 #include "dbmm_module.hxx"
22 #include "dbmm_types.hxx"
23 #include "docinteraction.hxx"
24 #include "migrationengine.hxx"
25 #include "migrationerror.hxx"
26 #include "migrationprogress.hxx"
27 #include "migrationlog.hxx"
28 #include "progresscapture.hxx"
29 #include "progressmixer.hxx"
31 #include <com/sun/star/frame/XModel.hpp>
32 #include <com/sun/star/ucb/XCommandProcessor.hpp>
33 #include <com/sun/star/ucb/XContent.hpp>
34 #include <com/sun/star/embed/ElementModes.hpp>
35 #include <com/sun/star/document/XStorageBasedDocument.hpp>
36 #include <com/sun/star/embed/XTransactedObject.hpp>
37 #include <com/sun/star/frame/XStorable.hpp>
38 #include <com/sun/star/script/DocumentScriptLibraryContainer.hpp>
39 #include <com/sun/star/script/DocumentDialogLibraryContainer.hpp>
40 #include <com/sun/star/document/XEmbeddedScripts.hpp>
41 #include <com/sun/star/document/XEventsSupplier.hpp>
42 #include <com/sun/star/uri/UriReferenceFactory.hpp>
43 #include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp>
44 #include <com/sun/star/form/XFormsSupplier.hpp>
45 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
46 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
47 #include <com/sun/star/script/XEventAttacherManager.hpp>
48 #include <com/sun/star/script/XLibraryContainerPassword.hpp>
49 #include <com/sun/star/io/WrongFormatException.hpp>
50 #include <com/sun/star/script/XScriptEventsSupplier.hpp>
51 #include <com/sun/star/io/XInputStreamProvider.hpp>
53 #include <comphelper/documentinfo.hxx>
54 #include <comphelper/interaction.hxx>
55 #include <comphelper/namedvaluecollection.hxx>
56 #include <comphelper/storagehelper.hxx>
57 #include <comphelper/types.hxx>
58 #include <cppuhelper/exc_hlp.hxx>
59 #include <tools/diagnose_ex.h>
60 #include <rtl/ustrbuf.hxx>
61 #include <rtl/ref.hxx>
62 #include <unotools/sharedunocomponent.hxx>
63 #include <xmlscript/xmldlg_imexp.hxx>
65 #include <vector>
66 #include <set>
67 #include <iterator>
69 #define DEFAULT_DOC_PROGRESS_RANGE 100000
71 namespace dbmm
74 using ::com::sun::star::uno::Reference;
75 using ::com::sun::star::uno::XInterface;
76 using ::com::sun::star::uno::UNO_QUERY;
77 using ::com::sun::star::uno::UNO_QUERY_THROW;
78 using ::com::sun::star::uno::UNO_SET_THROW;
79 using ::com::sun::star::uno::Exception;
80 using ::com::sun::star::uno::RuntimeException;
81 using ::com::sun::star::uno::Any;
82 using ::com::sun::star::uno::makeAny;
83 using ::com::sun::star::uno::XComponentContext;
84 using ::com::sun::star::sdb::XOfficeDatabaseDocument;
85 using ::com::sun::star::container::XNameAccess;
86 using ::com::sun::star::uno::Sequence;
87 using ::com::sun::star::lang::XComponent;
88 using ::com::sun::star::frame::XModel;
89 using ::com::sun::star::ucb::XCommandProcessor;
90 using ::com::sun::star::ucb::XContent;
91 using ::com::sun::star::ucb::Command;
92 using ::com::sun::star::task::XStatusIndicator;
93 using ::com::sun::star::embed::XStorage;
94 using ::com::sun::star::document::XStorageBasedDocument;
95 using ::com::sun::star::embed::XTransactedObject;
96 using ::com::sun::star::frame::XStorable;
97 using ::com::sun::star::script::DocumentDialogLibraryContainer;
98 using ::com::sun::star::script::DocumentScriptLibraryContainer;
99 using ::com::sun::star::script::XStorageBasedLibraryContainer;
100 using ::com::sun::star::document::XEmbeddedScripts;
101 using ::com::sun::star::container::XNameContainer;
102 using ::com::sun::star::document::XEventsSupplier;
103 using ::com::sun::star::container::XNameReplace;
104 using com::sun::star::uri::UriReferenceFactory;
105 using com::sun::star::uri::XUriReferenceFactory;
106 using com::sun::star::uri::XVndSunStarScriptUrlReference;
107 using ::com::sun::star::form::XFormsSupplier;
108 using ::com::sun::star::drawing::XDrawPageSupplier;
109 using ::com::sun::star::drawing::XDrawPagesSupplier;
110 using ::com::sun::star::drawing::XDrawPage;
111 using ::com::sun::star::drawing::XDrawPages;
112 using ::com::sun::star::container::XIndexAccess;
113 using ::com::sun::star::script::XEventAttacherManager;
114 using ::com::sun::star::script::ScriptEventDescriptor;
115 using ::com::sun::star::script::XLibraryContainerPassword;
116 using ::com::sun::star::io::WrongFormatException;
117 using ::com::sun::star::script::XScriptEventsSupplier;
118 using ::com::sun::star::io::XInputStreamProvider;
119 using ::com::sun::star::io::XInputStream;
121 namespace ElementModes = ::com::sun::star::embed::ElementModes;
123 // migration phases whose progresses are to be mixed into one progress
124 #define PHASE_JAVASCRIPT 1
125 #define PHASE_BEANSHELL 2
126 #define PHASE_PYTHON 3
127 #define PHASE_JAVA 4
128 #define PHASE_BASIC 5
129 #define PHASE_DIALOGS 6
131 // SubDocument
132 struct SubDocument
134 Reference< XCommandProcessor > xCommandProcessor;
135 Reference< XModel > xDocument; // valid only temporarily
136 OUString sHierarchicalName;
137 SubDocumentType eType;
138 size_t nNumber;
140 SubDocument( const Reference< XCommandProcessor >& _rxCommandProcessor, const OUString& _rName,
141 const SubDocumentType _eType, const size_t _nNumber )
142 :xCommandProcessor( _rxCommandProcessor )
143 ,xDocument()
144 ,sHierarchicalName( _rName )
145 ,eType( _eType )
146 ,nNumber( _nNumber )
151 typedef ::std::vector< SubDocument > SubDocuments;
153 // helper
154 typedef ::utl::SharedUNOComponent< XStorage > SharedStorage;
156 namespace
158 static const char sScriptsStorageName[] = "Scripts";
160 OUString lcl_getScriptsSubStorageName( const ScriptType _eType )
162 switch ( _eType )
164 case eBeanShell: return OUString("beanshell");
165 case eJavaScript: return OUString("javascript");
166 case ePython: return OUString("python"); // TODO: is this correct?
167 case eJava: return OUString("java");
168 default:
169 break;
172 OSL_FAIL( "lcl_getScriptsSubStorageName: illegal type!" );
173 return OUString();
176 bool lcl_getScriptTypeFromLanguage( const OUString& _rLanguage, ScriptType& _out_rScriptType )
178 struct LanguageMapping
180 const char* pAsciiLanguage;
181 const ScriptType eScriptType;
183 LanguageMapping( const char* _pAsciiLanguage, const ScriptType _eScriptType )
184 :pAsciiLanguage( _pAsciiLanguage )
185 ,eScriptType( _eScriptType )
189 const LanguageMapping aLanguageMapping[] =
191 LanguageMapping( "JavaScript", eJavaScript ),
192 LanguageMapping( "BeanShell", eBeanShell ),
193 LanguageMapping( "Java", eJava ),
194 LanguageMapping( "Python", ePython ), // TODO: is this correct?
195 LanguageMapping( "Basic", eBasic )
197 for (const LanguageMapping& i : aLanguageMapping)
199 if ( _rLanguage.equalsAscii( i.pAsciiLanguage ) )
201 _out_rScriptType = i.eScriptType;
202 return true;
205 OSL_FAIL( "lcl_getScriptTypeFromLanguage: unknown language!" );
206 return false;
209 OUString lcl_getSubDocumentDescription( const SubDocument& _rDocument )
211 OUString sObjectName(
212 MacroMigrationResId(
213 _rDocument.eType == eForm ? STR_FORM : STR_REPORT).toString().
214 replaceFirst("$name$", _rDocument.sHierarchicalName));
215 return sObjectName;
218 Any lcl_executeCommand_throw( const Reference< XCommandProcessor >& _rxCommandProc,
219 const sal_Char* _pAsciiCommand )
221 OSL_PRECOND( _rxCommandProc.is(), "lcl_executeCommand_throw: illegal object!" );
222 if ( !_rxCommandProc.is() )
223 return Any();
225 Command aCommand;
226 aCommand.Name = OUString::createFromAscii( _pAsciiCommand );
227 return _rxCommandProc->execute(
228 aCommand, _rxCommandProc->createCommandIdentifier(), nullptr );
231 OUString lcl_getMimeType_nothrow( const Reference< XCommandProcessor >& _rxContent )
233 OUString sMimeType;
236 Reference< XContent > xContent( _rxContent, UNO_QUERY_THROW );
237 sMimeType = xContent->getContentType();
239 catch( const Exception& )
241 DBG_UNHANDLED_EXCEPTION();
243 return sMimeType;
246 enum OpenDocResult
248 eOpenedDoc,
249 eIgnoreDoc,
250 eFailure
253 OpenDocResult lcl_loadSubDocument_nothrow( SubDocument& _rDocument,
254 const Reference< XStatusIndicator >& _rxProgress, MigrationLog& _rLogger )
256 OSL_PRECOND( !_rDocument.xDocument.is(), "lcl_loadSubDocument_nothrow: already loaded!" );
260 ::comphelper::NamedValueCollection aLoadArgs;
261 aLoadArgs.put( "Hidden", true );
262 aLoadArgs.put( "StatusIndicator", _rxProgress );
264 Reference< XCommandProcessor > xCommandProcessor( _rDocument.xCommandProcessor, UNO_SET_THROW );
265 Command aCommand;
266 aCommand.Name = "openDesign";
267 aCommand.Argument <<= aLoadArgs.getPropertyValues();
268 Reference< XComponent > xDocComponent(
269 xCommandProcessor->execute(
270 aCommand, xCommandProcessor->createCommandIdentifier(), nullptr
272 UNO_QUERY
274 OSL_ENSURE( xDocComponent.is(), "lcl_loadSubDocument_nothrow: no component loaded!" );
276 _rDocument.xDocument.set( xDocComponent, UNO_QUERY_THROW );
278 catch( const Exception& )
280 Any aError( ::cppu::getCaughtException() );
282 bool bCausedByNewStyleReport =
283 ( _rDocument.eType == eReport )
284 && ( aError.isExtractableTo( ::cppu::UnoType< WrongFormatException >::get() ) )
285 && ( lcl_getMimeType_nothrow( _rDocument.xCommandProcessor ) == "application/vnd.sun.xml.report" );
287 if ( bCausedByNewStyleReport )
289 _rLogger.logRecoverable( MigrationError(
290 ERR_NEW_STYLE_REPORT,
291 lcl_getSubDocumentDescription( _rDocument )
292 ) );
293 return eIgnoreDoc;
295 else
297 _rLogger.logFailure( MigrationError(
298 ERR_OPENING_SUB_DOCUMENT_FAILED,
299 lcl_getSubDocumentDescription( _rDocument ),
300 aError
301 ) );
304 return _rDocument.xDocument.is() ? eOpenedDoc : eFailure;
307 bool lcl_unloadSubDocument_nothrow( SubDocument& _rDocument, MigrationLog& _rLogger )
309 bool bSuccess = false;
310 Any aException;
313 OSL_VERIFY( lcl_executeCommand_throw( _rDocument.xCommandProcessor, "close" ) >>= bSuccess );
315 catch( const Exception& )
317 aException = ::cppu::getCaughtException();
320 // log the failure, if any
321 if ( !bSuccess )
323 _rLogger.logFailure( MigrationError(
324 ERR_CLOSING_SUB_DOCUMENT_FAILED,
325 lcl_getSubDocumentDescription( _rDocument ),
326 aException
327 ) );
330 _rDocument.xDocument.clear();
331 return bSuccess;
334 bool lcl_commitStorage_nothrow( const Reference< XStorage >& _rxStorage )
338 Reference< XTransactedObject > xTrans( _rxStorage, UNO_QUERY_THROW );
339 xTrans->commit();
341 catch( const Exception& )
343 return false;
345 return true;
348 bool lcl_commitDocumentStorage_nothrow( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger )
350 bool bSuccess = false;
351 Any aException;
354 Reference< XStorageBasedDocument > xStorageDoc( _rxDocument, UNO_QUERY_THROW );
355 Reference< XStorage > xDocStorage( xStorageDoc->getDocumentStorage(), UNO_QUERY_THROW );
356 bSuccess = lcl_commitStorage_nothrow( xDocStorage );
358 catch( const Exception& )
360 aException = ::cppu::getCaughtException();
363 // log the failure, if any
364 if ( !bSuccess )
366 _rLogger.logFailure( MigrationError(
367 ERR_STORAGE_COMMIT_FAILED,
368 ::comphelper::DocumentInfo::getDocumentTitle( _rxDocument ),
369 aException
370 ) );
372 return bSuccess;
375 bool lcl_storeDocument_nothrow( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger )
377 bool bSuccess = false;
378 Any aException;
381 Reference< XStorable > xStorable( _rxDocument, UNO_QUERY_THROW );
382 xStorable->store();
383 bSuccess = true;
385 catch( const Exception& )
387 aException = ::cppu::getCaughtException();
390 // log the failure, if any
391 if ( !bSuccess )
393 _rLogger.logFailure( MigrationError(
394 ERR_STORING_DATABASEDOC_FAILED,
395 aException
396 ) );
398 return bSuccess;
401 bool lcl_storeEmbeddedDocument_nothrow( const SubDocument& _rDocument )
405 lcl_executeCommand_throw( _rDocument.xCommandProcessor, "store" );
407 catch( const Exception& )
409 DBG_UNHANDLED_EXCEPTION();
410 return false;
412 return true;
416 // DrawPageIterator
417 class DrawPageIterator
419 public:
420 explicit DrawPageIterator( const Reference< XModel >& _rxDocument )
421 :m_nPageCount( 0 )
422 ,m_nCurrentPage( 0 )
424 Reference< XDrawPageSupplier > xSingle( _rxDocument, UNO_QUERY );
425 Reference< XDrawPagesSupplier > xMulti( _rxDocument, UNO_QUERY );
426 if ( xSingle.is() )
428 m_xSinglePage.set( xSingle->getDrawPage(), UNO_SET_THROW );
429 m_nPageCount = 1;
431 else if ( xMulti.is() )
433 m_xMultiPages.set( xMulti->getDrawPages(), UNO_SET_THROW );
434 m_nPageCount = m_xMultiPages->getCount();
438 bool hasMore() const
440 return m_nCurrentPage < m_nPageCount;
443 Reference< XDrawPage > next()
445 Reference< XDrawPage > xNextPage;
447 if ( m_xSinglePage.is() )
449 xNextPage = m_xSinglePage;
451 else if ( m_xMultiPages.is() )
453 xNextPage.set( m_xMultiPages->getByIndex( m_nCurrentPage ), UNO_QUERY_THROW );
455 ++m_nCurrentPage;
456 return xNextPage;
459 private:
460 Reference< XDrawPage > m_xSinglePage;
461 Reference< XDrawPages > m_xMultiPages;
462 sal_Int32 m_nPageCount;
463 sal_Int32 m_nCurrentPage;
466 // FormComponentScripts
467 class FormComponentScripts
469 public:
470 FormComponentScripts(
471 const Reference< XInterface >& _rxComponent,
472 const Reference< XEventAttacherManager >& _rxManager,
473 const sal_Int32 _nIndex
475 :m_xComponent( _rxComponent )
476 ,m_xManager( _rxManager )
477 ,m_nIndex( _nIndex )
481 Sequence< ScriptEventDescriptor > getEvents() const
483 return m_xManager->getScriptEvents( m_nIndex );
486 void setEvents( const Sequence< ScriptEventDescriptor >& _rEvents ) const
488 m_xManager->registerScriptEvents( m_nIndex, _rEvents );
491 const Reference< XInterface >& getComponent() const
493 return m_xComponent;
496 private:
497 const Reference< XInterface > m_xComponent;
498 const Reference< XEventAttacherManager > m_xManager;
499 const sal_Int32 m_nIndex;
502 // FormComponentIterator
503 class FormComponentIterator
505 public:
506 explicit FormComponentIterator( const Reference< XIndexAccess >& _rxContainer )
507 :m_xContainer( _rxContainer )
508 ,m_xEventManager( _rxContainer, UNO_QUERY_THROW )
509 ,m_nElementCount( _rxContainer->getCount() )
510 ,m_nCurrentElement( 0 )
514 bool hasMore() const
516 return m_nCurrentElement < m_nElementCount;
519 FormComponentScripts next()
521 FormComponentScripts aComponent(
522 Reference< XInterface >( m_xContainer->getByIndex( m_nCurrentElement ), UNO_QUERY_THROW ),
523 m_xEventManager,
524 m_nCurrentElement
526 ++m_nCurrentElement;
527 return aComponent;
530 private:
531 const Reference< XIndexAccess > m_xContainer;
532 const Reference< XEventAttacherManager > m_xEventManager;
533 const sal_Int32 m_nElementCount;
534 sal_Int32 m_nCurrentElement;
538 // ScriptsStorage - declaration
539 /** a helper class which encapsulates access to the storages for Java/Script, BeanShell, and Python scripts,
540 i.e. all script types which can be manipulated on storage level.
542 class ScriptsStorage
544 public:
545 explicit ScriptsStorage( MigrationLog& _rLogger );
546 ScriptsStorage( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger );
547 ~ScriptsStorage();
549 /** determines whether the instance is valid, i.e. refers to a valid root storage
550 for reading/storing scripts
552 inline bool isValid() const { return m_xScriptsStorage.is(); }
554 /** binds the instance to a new document. Only to be called when the instance is not yet
555 bound (i.e. isValid returns <FALSE/>).
557 void bind( const Reference< XModel >& _rxDocument );
559 /// determines whether scripts of the given type are present
560 bool hasScripts( const ScriptType _eType ) const;
562 /// returns the root storage for the scripts of the given type
563 SharedStorage
564 getScriptsRoot( const ScriptType _eType ) const;
566 /** returns the names of the elements in the "Scripts" storage
568 ::std::set< OUString >
569 getElementNames() const;
571 /** removes the sub storage for a given script type
572 @precond
573 the respective storage is empty
574 @precond
575 the ScriptsStorage instance was opened for writing
577 void removeScriptTypeStorage( const ScriptType _eType ) const;
579 /** commits the changes at our XStorage object
581 bool commit();
583 /** removes the "Scripts" sub storage from the given document's root storage
584 @precond
585 the "Scripts" storage is empty
587 static bool
588 removeFromDocument( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger );
590 private:
591 MigrationLog& m_rLogger;
592 SharedStorage m_xScriptsStorage;
595 // ScriptsStorage - implementation
596 ScriptsStorage::ScriptsStorage( MigrationLog& _rLogger )
597 :m_rLogger( _rLogger )
598 ,m_xScriptsStorage()
602 ScriptsStorage::ScriptsStorage( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger )
603 :m_rLogger( _rLogger )
604 ,m_xScriptsStorage()
606 bind( _rxDocument );
609 ScriptsStorage::~ScriptsStorage()
613 bool ScriptsStorage::commit()
615 return lcl_commitStorage_nothrow( m_xScriptsStorage );
618 void ScriptsStorage::bind( const Reference< XModel >& _rxDocument )
620 OSL_PRECOND( !isValid(), "ScriptsStorage:bind: did not bother, yet, to check whether this is allowed!" );
623 Reference< XStorageBasedDocument > xStorageDoc( _rxDocument, UNO_QUERY_THROW );
624 Reference< XStorage > xDocStorage( xStorageDoc->getDocumentStorage(), UNO_QUERY_THROW );
626 // the "Scripts" storage exist, or if it does not (yet) exist and we are in write mode
627 // => open the storage
628 if ( ( xDocStorage->hasByName( sScriptsStorageName )
629 && xDocStorage->isStorageElement( sScriptsStorageName )
631 || !xDocStorage->hasByName( sScriptsStorageName )
634 m_xScriptsStorage.set(
635 xDocStorage->openStorageElement(
636 sScriptsStorageName, ElementModes::READWRITE
638 UNO_QUERY_THROW
642 catch( const Exception& )
644 m_rLogger.logFailure( MigrationError(
645 ERR_BIND_SCRIPT_STORAGE_FAILED,
646 ::comphelper::DocumentInfo::getDocumentTitle( _rxDocument ),
647 ::cppu::getCaughtException()
648 ) );
652 bool ScriptsStorage::hasScripts( const ScriptType _eType ) const
654 OSL_PRECOND( isValid(), "ScriptsStorage::hasScripts: illegal call!" );
655 if ( !isValid() )
656 return false;
658 const OUString& rSubStorageName( lcl_getScriptsSubStorageName( _eType ) );
659 return m_xScriptsStorage->hasByName( rSubStorageName )
660 && m_xScriptsStorage->isStorageElement( rSubStorageName );
663 SharedStorage ScriptsStorage::getScriptsRoot( const ScriptType _eType ) const
665 SharedStorage xStorage;
666 if ( isValid() )
668 xStorage.reset( m_xScriptsStorage->openStorageElement(
669 lcl_getScriptsSubStorageName( _eType ), ElementModes::READWRITE
670 ) );
672 return xStorage;
675 ::std::set< OUString > ScriptsStorage::getElementNames() const
677 Sequence< OUString > aElementNames;
678 if ( isValid() )
679 aElementNames = m_xScriptsStorage->getElementNames();
681 ::std::set< OUString > aNames;
682 ::std::copy(
683 aElementNames.getConstArray(),
684 aElementNames.getConstArray() + aElementNames.getLength(),
685 ::std::insert_iterator< ::std::set< OUString > >( aNames, aNames.end() )
687 return aNames;
690 void ScriptsStorage::removeScriptTypeStorage( const ScriptType _eType ) const
692 OUString sSubStorageName( lcl_getScriptsSubStorageName( _eType ) );
693 if ( m_xScriptsStorage->hasByName( sSubStorageName ) )
694 m_xScriptsStorage->removeElement( sSubStorageName );
697 bool ScriptsStorage::removeFromDocument( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger )
701 Reference< XStorageBasedDocument > xStorageDoc( _rxDocument, UNO_QUERY_THROW );
702 Reference< XStorage > xDocStorage( xStorageDoc->getDocumentStorage(), UNO_QUERY_THROW );
703 xDocStorage->removeElement( sScriptsStorageName );
705 catch( const Exception& )
707 _rLogger.logFailure( MigrationError(
708 ERR_REMOVE_SCRIPTS_STORAGE_FAILED,
709 ::comphelper::DocumentInfo::getDocumentTitle( _rxDocument ),
710 ::cppu::getCaughtException()
711 ) ) ;
712 return false;
714 return true;
717 // ProgressDelegator
718 class ProgressDelegator : public IProgressConsumer
720 public:
721 ProgressDelegator( IMigrationProgress& _rDelegator,
722 const OUString& _rObjectName,
723 const OUString& _rAction
725 :m_rDelegator( _rDelegator )
726 ,m_sObjectName( _rObjectName )
727 ,m_sAction( _rAction )
730 virtual ~ProgressDelegator()
734 // IProgressConsumer
735 virtual void start( sal_uInt32 _nRange ) override
737 m_rDelegator.startObject( m_sObjectName, m_sAction, _nRange );
739 virtual void advance( sal_uInt32 _nValue ) override
741 m_rDelegator.setObjectProgressValue( _nValue );
743 virtual void end() override
745 m_rDelegator.endObject();
748 private:
749 IMigrationProgress& m_rDelegator;
750 OUString m_sObjectName;
751 OUString m_sAction;
754 // PhaseGuard
755 class PhaseGuard
757 public:
758 explicit PhaseGuard( ProgressMixer& _rMixer )
759 :m_rMixer( _rMixer )
763 ~PhaseGuard()
765 m_rMixer.endPhase();
768 void start( const PhaseID _nID, const sal_uInt32 _nPhaseRange )
770 m_rMixer.startPhase( _nID, _nPhaseRange );
773 private:
774 ProgressMixer& m_rMixer;
777 // MigrationEngine_Impl - declaration
778 class MigrationEngine_Impl
780 public:
781 MigrationEngine_Impl(
782 const Reference<XComponentContext>& _rContext,
783 const Reference< XOfficeDatabaseDocument >& _rxDocument,
784 IMigrationProgress& _rProgress,
785 MigrationLog& _rLogger
787 ~MigrationEngine_Impl();
789 inline size_t getFormCount() const { return m_nFormCount; }
790 inline size_t getReportCount()const { return m_nReportCount; }
791 bool migrateAll();
793 private:
794 Reference<XComponentContext> m_aContext;
795 const Reference< XOfficeDatabaseDocument > m_xDocument;
796 const Reference< XModel > m_xDocumentModel;
797 IMigrationProgress& m_rProgress;
798 MigrationLog& m_rLogger;
799 mutable DocumentID m_nCurrentDocumentID;
800 SubDocuments m_aSubDocs;
801 size_t m_nFormCount;
802 size_t m_nReportCount;
804 private:
805 /** collects a description of all sub documents of our database document
807 @return
808 <TRUE/> if and only if collecting the documents was successful
810 bool impl_collectSubDocuments_nothrow();
812 /** migrates the macros/scripts of the given sub document
814 bool impl_handleDocument_nothrow( const SubDocument& _rDocument ) const;
816 /** checks the structure of the 'Scripts' folder of a sub document
817 for unknown elements
819 @return
820 <TRUE/> if and only if the 'Scripts' folder contains known elements only.
822 bool impl_checkScriptStorageStructure_nothrow( const SubDocument& _rDocument ) const;
824 /** migrates the scripts of the given "storage-based" script type
826 bool impl_migrateScriptStorage_nothrow(
827 const SubDocument& _rDocument,
828 const ScriptType _eScriptType,
829 ProgressMixer& _rProgress,
830 const PhaseID _nPhaseID
831 ) const;
833 /** migrates the content of the given "container based" libraries (Basic/Dialogs)
835 bool impl_migrateContainerLibraries_nothrow(
836 const SubDocument& _rDocument,
837 const ScriptType _eScriptType,
838 ProgressMixer& _rProgress,
839 const PhaseID _nPhaseID
840 ) const;
842 /** adjusts the events for the given dialog/element, taking into account the new names
843 of the moved libraries
845 void impl_adjustDialogElementEvents_throw(
846 const Reference< XInterface >& _rxElement
847 ) const;
849 /** adjusts the events in the given dialog, and its controls, taking into account the new names
850 of the moved libraries
852 bool impl_adjustDialogEvents_nothrow(
853 Any& _inout_rDialogLibraryElement,
854 const OUString& _rDocName,
855 const OUString& _rDialogLibName,
856 const OUString& _rDialogName
857 ) const;
859 /** adjust the document-events which refer to macros/scripts in the document, taking into
860 account the new names of the moved libraries
862 void impl_adjustDocumentEvents_nothrow(
863 const SubDocument& _rDocument
864 ) const;
866 /** adjusts the script references bound to form component events
868 void impl_adjustFormComponentEvents_nothrow(
869 const SubDocument& _rDocument
870 ) const;
872 /** adjusts the script references for the elements of the given form component container
874 void impl_adjustFormComponentEvents_throw(
875 const Reference< XIndexAccess >& _rxComponentContainer
876 ) const;
878 /** adjusts the library name in the given script URL, so that it reflects
879 the new name of the library
881 @return <TRUE/>
882 if and only if adjustments to the script code have been made
884 bool impl_adjustScriptLibrary_nothrow(
885 const OUString& _rScriptType,
886 OUString& _inout_rScriptCode
887 ) const;
889 bool impl_adjustScriptLibrary_nothrow( Any& _inout_rScriptDescriptor ) const;
890 bool impl_adjustScriptLibrary_nothrow( ScriptEventDescriptor& _inout_rScriptEvent ) const;
892 /** asks the user for a password for the given library, and unprotects the library
894 @return <TRUE/>
895 if and only if the library could be successfully unprotected
897 bool impl_unprotectPasswordLibrary_throw(
898 const Reference< XLibraryContainerPassword >& _rxPasswordManager,
899 const ScriptType _eScriptType,
900 const OUString& _rLibraryName
901 ) const;
904 // MigrationEngine_Impl - implementation
905 MigrationEngine_Impl::MigrationEngine_Impl( const Reference<XComponentContext>& _rContext,
906 const Reference< XOfficeDatabaseDocument >& _rxDocument, IMigrationProgress& _rProgress, MigrationLog& _rLogger )
907 :m_aContext( _rContext )
908 ,m_xDocument( _rxDocument )
909 ,m_xDocumentModel( _rxDocument, UNO_QUERY_THROW )
910 ,m_rProgress( _rProgress )
911 ,m_rLogger( _rLogger )
912 ,m_nCurrentDocumentID( - 1 )
913 ,m_aSubDocs()
914 ,m_nFormCount( 0 )
915 ,m_nReportCount( 0 )
917 OSL_VERIFY( impl_collectSubDocuments_nothrow() );
920 MigrationEngine_Impl::~MigrationEngine_Impl()
924 bool MigrationEngine_Impl::migrateAll()
926 if ( m_aSubDocs.empty() )
928 OSL_FAIL( "MigrationEngine_Impl::migrateAll: no forms/reports found!" );
929 // The whole migration wizard is not expected to be called when there are no forms/reports
930 // with macros, not to mention when there are no forms/reports at all.
931 return false;
934 // initialize global progress
935 sal_Int32 nOverallRange( m_aSubDocs.size() );
936 OUString sProgressSkeleton(
937 MacroMigrationResId( STR_OVERALL_PROGRESS).toString().
938 replaceFirst("$overall$", OUString::number(nOverallRange)));
940 m_rProgress.start( nOverallRange );
942 for ( SubDocuments::const_iterator doc = m_aSubDocs.begin();
943 doc != m_aSubDocs.end();
944 ++doc
947 sal_Int32 nOverallProgressValue( doc - m_aSubDocs.begin() + 1 );
948 // update overall progress text
949 OUString sOverallProgress(
950 sProgressSkeleton.replaceFirst("$current$",
951 OUString::number(nOverallProgressValue)));
952 m_rProgress.setOverallProgressText( sOverallProgress );
954 // migrate document
955 if ( !impl_handleDocument_nothrow( *doc ) )
956 return false;
958 // update overall progress value
959 m_rProgress.setOverallProgressValue( nOverallProgressValue );
962 // commit the root storage of the database document, for all changes made so far to take effect
963 if ( !lcl_commitDocumentStorage_nothrow( m_xDocumentModel, m_rLogger ) )
964 return false;
966 // save the document
967 if ( !lcl_storeDocument_nothrow( m_xDocumentModel, m_rLogger ) )
968 return false;
970 return true;
973 namespace
975 void lcl_collectHierarchicalElementNames_throw(
976 const Reference< XNameAccess >& _rxContainer, const OUString& _rContainerLoc,
977 SubDocuments& _out_rDocs, const SubDocumentType _eType, size_t& _io_counter )
979 const OUString sHierarhicalBase(
980 _rContainerLoc.isEmpty() ? OUString() :
981 OUString( _rContainerLoc + "/" ) );
983 Sequence< OUString > aElementNames( _rxContainer->getElementNames() );
984 for ( const OUString* elementName = aElementNames.getConstArray();
985 elementName != aElementNames.getConstArray() + aElementNames.getLength();
986 ++elementName
989 Any aElement( _rxContainer->getByName( *elementName ) );
990 OUString sElementName( sHierarhicalBase + *elementName );
992 Reference< XNameAccess > xSubContainer( aElement, UNO_QUERY );
993 if ( xSubContainer.is() )
995 lcl_collectHierarchicalElementNames_throw( xSubContainer, sElementName, _out_rDocs, _eType, _io_counter );
997 else
999 Reference< XCommandProcessor > xCommandProcessor( aElement, UNO_QUERY );
1000 OSL_ENSURE( xCommandProcessor.is(), "lcl_collectHierarchicalElementNames_throw: no container, and no command processor? What *is* it, then?!" );
1001 if ( xCommandProcessor.is() )
1003 _out_rDocs.push_back( SubDocument( xCommandProcessor, sElementName, _eType, ++_io_counter ) );
1010 bool MigrationEngine_Impl::impl_collectSubDocuments_nothrow()
1012 OSL_PRECOND( m_xDocument.is(), "MigrationEngine_Impl::impl_collectSubDocuments_nothrow: invalid document!" );
1013 if ( !m_xDocument.is() )
1014 return false;
1018 Reference< XNameAccess > xDocContainer( m_xDocument->getFormDocuments(), UNO_SET_THROW );
1019 m_nFormCount = 0;
1020 lcl_collectHierarchicalElementNames_throw( xDocContainer, OUString(), m_aSubDocs, eForm, m_nFormCount );
1022 xDocContainer.set( m_xDocument->getReportDocuments(), UNO_SET_THROW );
1023 m_nReportCount = 0;
1024 lcl_collectHierarchicalElementNames_throw( xDocContainer, OUString(), m_aSubDocs, eReport, m_nReportCount );
1026 catch( const Exception& )
1028 m_rLogger.logFailure( MigrationError(
1029 ERR_COLLECTING_DOCUMENTS_FAILED,
1030 ::cppu::getCaughtException()
1031 ) );
1032 return false;
1034 return true;
1037 bool MigrationEngine_Impl::impl_handleDocument_nothrow( const SubDocument& _rDocument ) const
1039 OSL_ENSURE( m_nCurrentDocumentID == -1,
1040 "MigrationEngine_Impl::impl_handleDocument_nothrow: there already is a current document!");
1041 m_nCurrentDocumentID = m_rLogger.startedDocument( _rDocument.eType, _rDocument.sHierarchicalName );
1043 // start the progress
1044 OUString sObjectName( lcl_getSubDocumentDescription( _rDocument ) );
1045 m_rProgress.startObject( sObjectName, OUString(), DEFAULT_DOC_PROGRESS_RANGE );
1047 // load the document
1048 Reference< ProgressCapture > pStatusIndicator( new ProgressCapture( sObjectName, m_rProgress ) );
1049 SubDocument aSubDocument( _rDocument );
1050 OpenDocResult eResult = lcl_loadSubDocument_nothrow( aSubDocument, pStatusIndicator.get(), m_rLogger );
1051 if ( eResult != eOpenedDoc )
1053 pStatusIndicator->dispose();
1054 m_rProgress.endObject();
1055 m_rLogger.finishedDocument( m_nCurrentDocumentID );
1056 m_nCurrentDocumentID = -1;
1057 return ( eResult == eIgnoreDoc );
1060 // migrate the libraries
1061 ProgressDelegator aDelegator(m_rProgress, sObjectName, MacroMigrationResId(STR_MIGRATING_LIBS).toString());
1062 ProgressMixer aProgressMixer( aDelegator );
1063 aProgressMixer.registerPhase( PHASE_JAVASCRIPT, 1 );
1064 aProgressMixer.registerPhase( PHASE_BEANSHELL, 1 );
1065 aProgressMixer.registerPhase( PHASE_PYTHON, 1 );
1066 aProgressMixer.registerPhase( PHASE_JAVA, 1 );
1067 aProgressMixer.registerPhase( PHASE_BASIC, 5 );
1068 // more weight than the others, assuming that usually, there are many more Basic macros than any other scripts
1069 aProgressMixer.registerPhase( PHASE_DIALOGS, 1 );
1071 bool bSuccess = impl_checkScriptStorageStructure_nothrow( aSubDocument );
1073 // migrate storage-based script libraries (which can be handled by mere storage operations)
1074 bSuccess = bSuccess
1075 && impl_migrateScriptStorage_nothrow( aSubDocument, eJavaScript, aProgressMixer, PHASE_JAVASCRIPT )
1076 && impl_migrateScriptStorage_nothrow( aSubDocument, eBeanShell, aProgressMixer, PHASE_BEANSHELL )
1077 && impl_migrateScriptStorage_nothrow( aSubDocument, ePython, aProgressMixer, PHASE_PYTHON )
1078 && impl_migrateScriptStorage_nothrow( aSubDocument, eJava, aProgressMixer, PHASE_JAVA );
1080 // migrate Basic and dialog libraries
1081 bSuccess = bSuccess
1082 && impl_migrateContainerLibraries_nothrow( aSubDocument, eBasic, aProgressMixer, PHASE_BASIC )
1083 && impl_migrateContainerLibraries_nothrow( aSubDocument, eDialog, aProgressMixer, PHASE_DIALOGS );
1084 // order matters: First Basic scripts, then dialogs. So we can adjust references from the latter
1085 // to the former
1087 // adjust the events in the document
1088 // (note that errors are ignored here - failure to convert a script reference
1089 // is not considered a critical error)
1090 if ( bSuccess )
1092 impl_adjustDocumentEvents_nothrow( aSubDocument );
1093 impl_adjustFormComponentEvents_nothrow( aSubDocument );
1096 // clean up
1097 // store the sub document, including removal of the (now obsolete) "Scripts" sub folder
1098 if ( m_rLogger.movedAnyLibrary( m_nCurrentDocumentID ) )
1100 bSuccess = bSuccess
1101 && ScriptsStorage::removeFromDocument( aSubDocument.xDocument, m_rLogger )
1102 && lcl_commitDocumentStorage_nothrow( aSubDocument.xDocument, m_rLogger )
1103 && lcl_storeEmbeddedDocument_nothrow( aSubDocument );
1106 // unload in any case, even if we were not successful
1107 bSuccess = lcl_unloadSubDocument_nothrow( aSubDocument, m_rLogger )
1108 && bSuccess;
1110 pStatusIndicator->dispose();
1112 // end the progress, just in case the ProgressCapture didn't receive the XStatusIndicator::end event
1113 m_rProgress.endObject();
1115 m_rLogger.finishedDocument( m_nCurrentDocumentID );
1116 m_nCurrentDocumentID = -1;
1117 return bSuccess;
1120 namespace
1122 OUString lcl_createTargetLibName( const SubDocument& _rDocument,
1123 const OUString& _rSourceLibName, const Reference< XNameAccess >& _rxTargetContainer )
1125 // The new library name is composed from the prefix, the base name, and the old library name.
1126 const OUString sPrefix = (_rDocument.eType == eForm)?OUString("Form_"): OUString("Report_");
1128 OUString sBaseName( _rDocument.sHierarchicalName.copy(
1129 _rDocument.sHierarchicalName.lastIndexOf( '/' ) + 1 ) );
1130 // Normalize this name. In our current storage implementation (and script containers in a document
1131 // are finally mapped to sub storages of the document storage), not all characters are allowed.
1132 // The bug requesting to change this is #i95409#.
1133 // Unfortunately, the storage implementation does not complain if you use invalid characters/names, but instead
1134 // it silently accepts them, and produces garbage in the file (#i95408).
1135 // So, until especially the former is fixed, we need to strip all invalid characters from the name.
1136 // #i95865#
1138 // The general idea is to replace invalid characters with '_'. However, since "valid" essentially means
1139 // ASCII only, this implies that for a lot of languages, we would simply replace everything with '_',
1140 // which of course is not desired.
1141 // So, we use a heuristics: If the name contains at most 3 invalid characters, and as many valid as invalid
1142 // characters, then we use the replacement. Otherwise, we just use a unambiguous number for the sub document.
1143 sal_Int32 nValid=0, nInvalid=0;
1144 const sal_Unicode* pBaseName = sBaseName.getStr();
1145 const sal_Int32 nBaseNameLen = sBaseName.getLength();
1146 for ( sal_Int32 i=0; i<nBaseNameLen; ++i )
1148 if ( ::comphelper::OStorageHelper::IsValidZipEntryFileName( pBaseName + i, 1, false ) )
1149 ++nValid;
1150 else
1151 ++nInvalid;
1153 if ( ( nInvalid <= 3 ) && ( nInvalid * 2 <= nValid ) )
1154 { // not "too many" invalid => replace them
1155 OUStringBuffer aReplacement;
1156 aReplacement.ensureCapacity( nBaseNameLen );
1157 aReplacement.append( sBaseName );
1158 const sal_Unicode* pReplacement = aReplacement.getStr();
1159 for ( sal_Int32 i=0; i<nBaseNameLen; ++i )
1161 if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( pReplacement + i, 1, false ) )
1162 aReplacement[i] = '_';
1164 sBaseName = aReplacement.makeStringAndClear();
1166 OUString sTargetName( sPrefix + sBaseName + "_" + _rSourceLibName );
1167 if ( !_rxTargetContainer->hasByName( sTargetName ) )
1168 return sTargetName;
1171 // "too many" invalid characters, or the name composed with the base name was already used.
1172 // (The latter is valid, since there can be multiple sub documents with the same base name,
1173 // in different levels in the hierarchy.)
1174 // In this case, just use the umambiguous sub document number.
1175 return sPrefix + OUString::number( _rDocument.nNumber ) + "_" + _rSourceLibName;
1179 bool MigrationEngine_Impl::impl_checkScriptStorageStructure_nothrow( const SubDocument& _rDocument ) const
1181 OSL_PRECOND( _rDocument.xDocument.is(), "MigrationEngine_Impl::impl_checkScriptStorageStructure_nothrow: invalid document!" );
1182 if ( !_rDocument.xDocument.is() )
1183 return false;
1187 // the root storage of the document whose scripts are to be migrated
1188 ScriptsStorage aDocStorage( _rDocument.xDocument, m_rLogger );
1189 if ( !aDocStorage.isValid() )
1190 { // no scripts at all, or no scripts of the given type
1191 return !m_rLogger.hadFailure();
1193 ::std::set< OUString > aElementNames( aDocStorage.getElementNames() );
1195 const ScriptType aKnownStorageBasedTypes[] = {
1196 eBeanShell, eJavaScript, ePython, eJava
1198 for (ScriptType aKnownStorageBasedType : aKnownStorageBasedTypes)
1199 aElementNames.erase( lcl_getScriptsSubStorageName( aKnownStorageBasedType ) );
1201 if ( !aElementNames.empty() )
1203 m_rLogger.logFailure( MigrationError(
1204 ERR_UNKNOWN_SCRIPT_FOLDER,
1205 lcl_getSubDocumentDescription( _rDocument ),
1206 *aElementNames.begin()
1207 ) );
1208 return false;
1211 catch( const Exception& )
1213 m_rLogger.logFailure( MigrationError(
1214 ERR_EXAMINING_SCRIPTS_FOLDER_FAILED,
1215 lcl_getSubDocumentDescription( _rDocument ),
1216 ::cppu::getCaughtException()
1217 ) );
1218 return false;
1220 return true;
1223 bool MigrationEngine_Impl::impl_migrateScriptStorage_nothrow( const SubDocument& _rDocument,
1224 const ScriptType _eScriptType, ProgressMixer& _rProgress, const PhaseID _nPhaseID ) const
1226 OSL_PRECOND( _rDocument.xDocument.is(), "MigrationEngine_Impl::impl_migrateScriptStorage_nothrow: invalid document!" );
1227 if ( !_rDocument.xDocument.is() )
1228 return false;
1230 ScriptsStorage aDatabaseScripts( m_rLogger );
1231 // the scripts of our complete database document - created on demand only
1232 SharedStorage xTargetStorage;
1233 // the target for moving the scripts storages - created on demand only
1235 PhaseGuard aPhase( _rProgress );
1236 bool bSuccess = false;
1237 Any aException;
1240 // the root storage of the document whose scripts are to be migrated
1241 ScriptsStorage aDocStorage( _rDocument.xDocument, m_rLogger );
1242 if ( !aDocStorage.isValid()
1243 || !aDocStorage.hasScripts( _eScriptType )
1246 // no scripts at all, or no scripts of the given type
1247 _rProgress.startPhase( _nPhaseID, 1 );
1248 _rProgress.endPhase();
1249 return !m_rLogger.hadFailure();
1252 SharedStorage xScriptsRoot( aDocStorage.getScriptsRoot( _eScriptType ) );
1253 if ( !xScriptsRoot.is() )
1254 throw RuntimeException("internal error");
1256 // loop through the script libraries
1257 Sequence< OUString > aStorageElements( xScriptsRoot->getElementNames() );
1258 aPhase.start( _nPhaseID, aStorageElements.getLength() );
1260 for ( const OUString* element = aStorageElements.getConstArray();
1261 element != aStorageElements.getConstArray() + aStorageElements.getLength();
1262 ++element
1265 bool bIsScriptLibrary = xScriptsRoot->isStorageElement( *element );
1266 OSL_ENSURE( bIsScriptLibrary,
1267 "MigrationEngine_Impl::impl_migrateScriptStorage_nothrow: warning: unknown scripts storage structure!" );
1268 // we cannot handle this. We would need to copy this stream to the respective scripts storage
1269 // of the database document, but we cannot guarantee that the name is not used, yet, and we cannot
1270 // simply rename the thing.
1271 if ( !bIsScriptLibrary )
1273 m_rLogger.logFailure( MigrationError(
1274 ERR_UNEXPECTED_LIBSTORAGE_ELEMENT,
1275 lcl_getSubDocumentDescription( _rDocument ),
1276 getScriptTypeDisplayName( _eScriptType ),
1277 *element
1278 ) );
1279 return false;
1282 // ensure we have access to the DBDoc's scripts storage
1283 if ( !aDatabaseScripts.isValid() )
1284 { // not needed 'til now
1285 aDatabaseScripts.bind( m_xDocumentModel );
1286 if ( aDatabaseScripts.isValid() )
1287 xTargetStorage = aDatabaseScripts.getScriptsRoot( _eScriptType );
1289 if ( !xTargetStorage.is() )
1291 m_rLogger.logFailure( MigrationError(
1292 ERR_CREATING_DBDOC_SCRIPT_STORAGE_FAILED,
1293 getScriptTypeDisplayName( _eScriptType )
1294 ) );
1295 return false;
1299 // move the library to the DBDoc's scripts library, under the new name
1300 OUString sNewLibName( lcl_createTargetLibName( _rDocument, *element, xTargetStorage.getTyped().get() ) );
1301 xScriptsRoot->moveElementTo( *element, xTargetStorage, sNewLibName );
1303 // log the fact that we moved the library
1304 m_rLogger.movedLibrary( m_nCurrentDocumentID, _eScriptType, *element, sNewLibName );
1306 // progress
1307 _rProgress.advancePhase( element - aStorageElements.getConstArray() );
1310 // commit the storages, so the changes we made persist
1311 if ( !lcl_commitStorage_nothrow( xScriptsRoot )
1312 || ( xTargetStorage.is() && !lcl_commitStorage_nothrow( xTargetStorage ) )
1315 m_rLogger.logFailure( MigrationError(
1316 ERR_COMMITTING_SCRIPT_STORAGES_FAILED,
1317 getScriptTypeDisplayName( _eScriptType ),
1318 lcl_getSubDocumentDescription( _rDocument )
1319 ) );
1320 return false;
1323 // now that the concrete scripts storage does not have any elements anymore,
1324 // remove it
1325 xScriptsRoot.reset( nullptr ); // need to reset the storage to be allowed to remove it
1326 aDocStorage.removeScriptTypeStorage( _eScriptType );
1328 // done so far
1329 bSuccess = aDocStorage.commit()
1330 && aDatabaseScripts.commit();
1332 catch( const Exception& )
1334 aException = ::cppu::getCaughtException();
1335 bSuccess = false;
1338 // log the error, if any
1339 if ( !bSuccess )
1341 m_rLogger.logFailure( MigrationError(
1342 ERR_GENERAL_SCRIPT_MIGRATION_FAILURE,
1343 getScriptTypeDisplayName( _eScriptType ),
1344 lcl_getSubDocumentDescription( _rDocument ),
1345 aException
1346 ) );
1349 return bSuccess;
1352 bool MigrationEngine_Impl::impl_migrateContainerLibraries_nothrow( const SubDocument& _rDocument,
1353 const ScriptType _eScriptType, ProgressMixer& _rProgress, const PhaseID _nPhaseID ) const
1355 OSL_PRECOND( ( _eScriptType == eBasic ) || ( _eScriptType == eDialog ),
1356 "MigrationEngine_Impl::impl_migrateContainerLibraries_nothrow: illegal script type!" );
1358 bool bSuccess = false;
1359 PhaseGuard aPhase( _rProgress );
1360 Any aException;
1361 do // artificial loop for flow control only
1365 // access library container of the sub document
1366 Reference< XEmbeddedScripts > xSubDocScripts( _rDocument.xDocument, UNO_QUERY );
1367 if ( !xSubDocScripts.is() )
1368 { // no script support in the sub document -> nothing to migrate
1369 // (though ... this is suspicious, at least ...)
1370 bSuccess = true;
1371 break;
1374 Reference< XStorageBasedLibraryContainer > xSourceLibraries(
1375 _eScriptType == eBasic ? xSubDocScripts->getBasicLibraries() : xSubDocScripts->getDialogLibraries(),
1376 UNO_QUERY_THROW
1378 Reference< XLibraryContainerPassword > xSourcePasswords( xSourceLibraries, UNO_QUERY );
1379 OSL_ENSURE( xSourcePasswords.is(),
1380 "MigrationEngine_Impl::impl_migrateContainerLibraries_nothrow: suspicious: no password management for the source libraries!" );
1382 Sequence< OUString > aSourceLibNames( xSourceLibraries->getElementNames() );
1383 aPhase.start( _nPhaseID, aSourceLibNames.getLength() );
1385 if ( !xSourceLibraries->hasElements() )
1387 bSuccess = true;
1388 break;
1391 // create library containers for the document - those will be the target for the migration
1392 Reference< XStorageBasedDocument > xStorageDoc( m_xDocument, UNO_QUERY_THROW );
1393 Reference< XStorageBasedLibraryContainer > xTargetLibraries;
1394 if ( _eScriptType == eBasic )
1396 xTargetLibraries.set( DocumentScriptLibraryContainer::create(
1397 m_aContext, xStorageDoc ), UNO_QUERY_THROW );
1399 else
1401 xTargetLibraries.set( DocumentDialogLibraryContainer::create(
1402 m_aContext, xStorageDoc ), UNO_QUERY_THROW );
1405 // copy all libs to the target, with potentially renaming them
1406 const OUString* pSourceLibBegin = aSourceLibNames.getConstArray();
1407 const OUString* pSourceLibEnd = pSourceLibBegin + aSourceLibNames.getLength();
1408 for ( const OUString* pSourceLibName = pSourceLibBegin;
1409 pSourceLibName != pSourceLibEnd;
1410 ++pSourceLibName
1413 // if the library is password-protected, ask the user to unprotect it
1414 if ( xSourcePasswords.is()
1415 && xSourcePasswords->isLibraryPasswordProtected( *pSourceLibName )
1416 && !xSourcePasswords->isLibraryPasswordVerified( *pSourceLibName )
1419 if ( !impl_unprotectPasswordLibrary_throw( xSourcePasswords, _eScriptType, *pSourceLibName ) )
1421 m_rLogger.logFailure( MigrationError(
1422 ERR_PASSWORD_VERIFICATION_FAILED,
1423 _rDocument.sHierarchicalName,
1424 getScriptTypeDisplayName( _eScriptType ),
1425 *pSourceLibName
1426 ) );
1427 return false;
1431 OUString sNewLibName( lcl_createTargetLibName( _rDocument, *pSourceLibName, xTargetLibraries.get() ) );
1433 if ( xSourceLibraries->isLibraryLink( *pSourceLibName ) )
1435 // just re-create the link in the target library
1436 xTargetLibraries->createLibraryLink(
1437 sNewLibName,
1438 xSourceLibraries->getLibraryLinkURL( *pSourceLibName ),
1439 xSourceLibraries->isLibraryReadOnly( *pSourceLibName )
1442 else
1444 if ( !xSourceLibraries->isLibraryLoaded( *pSourceLibName ) )
1445 xSourceLibraries->loadLibrary( *pSourceLibName );
1447 // copy the content of this particular library
1448 Reference< XNameAccess > xSourceLib( xSourceLibraries->getByName( *pSourceLibName ), UNO_QUERY_THROW );
1449 Reference< XNameContainer > xTargetLib( xTargetLibraries->createLibrary( sNewLibName ), UNO_QUERY_THROW );
1451 Sequence< OUString > aLibElementNames( xSourceLib->getElementNames() );
1452 for ( const OUString* pSourceElementName = aLibElementNames.getConstArray();
1453 pSourceElementName != aLibElementNames.getConstArray() + aLibElementNames.getLength();
1454 ++pSourceElementName
1457 Any aElement = xSourceLib->getByName( *pSourceElementName );
1458 OSL_ENSURE( aElement.hasValue(),
1459 "MigrationEngine_Impl::impl_migrateContainerLibraries_nothrow: invalid (empty) lib element!" );
1461 // if this is a dialog, adjust the references to scripts
1462 if ( _eScriptType == eDialog )
1464 impl_adjustDialogEvents_nothrow( aElement, lcl_getSubDocumentDescription( _rDocument ),
1465 *pSourceLibName, *pSourceElementName );
1468 xTargetLib->insertByName( *pSourceElementName, aElement );
1471 // transfer the read-only flag
1472 xTargetLibraries->setLibraryReadOnly(
1473 sNewLibName, xSourceLibraries->isLibraryReadOnly( *pSourceLibName ) );
1476 // remove the source lib
1477 xSourceLibraries->removeLibrary( *pSourceLibName );
1479 // tell the logger
1480 m_rLogger.movedLibrary( m_nCurrentDocumentID, _eScriptType, *pSourceLibName, sNewLibName );
1482 // tell the progress
1483 _rProgress.advancePhase( pSourceLibName - pSourceLibBegin );
1486 // clean up
1487 xSourceLibraries->storeLibraries();
1489 xTargetLibraries->storeLibraries();
1490 Reference< XStorage > xTargetRoot( xTargetLibraries->getRootLocation(), UNO_QUERY_THROW );
1491 bSuccess = lcl_commitStorage_nothrow( xTargetRoot );
1493 catch( const Exception& )
1495 aException = ::cppu::getCaughtException();
1496 bSuccess = false;
1498 } while ( false );
1500 // log the error, if any
1501 if ( !bSuccess )
1503 m_rLogger.logFailure( MigrationError(
1504 ERR_GENERAL_MACRO_MIGRATION_FAILURE,
1505 lcl_getSubDocumentDescription( _rDocument ),
1506 aException
1507 ) );
1510 return bSuccess;
1513 bool MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow( const OUString& _rScriptType,
1514 OUString& _inout_rScriptCode ) const
1516 OSL_PRECOND( !_inout_rScriptCode.isEmpty(), "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: invalid script!" );
1517 if ( _inout_rScriptCode.isEmpty() )
1518 return false;
1520 bool bSuccess = false;
1521 Any aException;
1524 if ( _rScriptType != "Script" || _rScriptType.isEmpty() )
1526 OSL_FAIL(
1527 "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: no or unknown script type!" );
1528 m_rLogger.logRecoverable( MigrationError(
1529 ERR_UNKNOWN_SCRIPT_TYPE,
1530 _rScriptType
1531 ) );
1532 return false;
1535 // analyze the script URI
1536 Reference< XUriReferenceFactory > xUriRefFac = UriReferenceFactory::create( m_aContext );
1537 Reference< XVndSunStarScriptUrlReference > xUri( xUriRefFac->parse( _inout_rScriptCode ), UNO_QUERY_THROW );
1539 OUString sScriptLanguage = xUri->getParameter( "language" );
1540 ScriptType eScriptType = eBasic;
1541 if ( !lcl_getScriptTypeFromLanguage( sScriptLanguage, eScriptType ) )
1543 OSL_FAIL(
1544 "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: unknown script language!" );
1545 m_rLogger.logRecoverable( MigrationError(
1546 ERR_UNKNOWN_SCRIPT_LANGUAGE,
1547 sScriptLanguage
1548 ) );
1549 return false;
1552 OUString sLocation = xUri->getParameter( "location" );
1553 if ( sLocation != "document" )
1555 // only document libraries must be migrated, of course
1556 return false;
1559 OUString sScriptName = xUri->getName();
1560 sal_Int32 nLibModuleSeparator = sScriptName.indexOf( '.' );
1561 if ( nLibModuleSeparator < 0 )
1563 OSL_FAIL(
1564 "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: invalid/unknown location format!" );
1565 m_rLogger.logRecoverable( MigrationError(
1566 ERR_UNKNOWN_SCRIPT_NAME_FORMAT,
1567 sScriptName
1568 ) );
1569 return false;
1572 // replace the library name
1573 OUString sLibrary = sScriptName.copy( 0, nLibModuleSeparator );
1574 OUString sNewLibName = m_rLogger.getNewLibraryName(
1575 m_nCurrentDocumentID, eScriptType, sLibrary );
1576 OSL_ENSURE( sLibrary != sNewLibName,
1577 "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: a library which has not been migrated?" );
1579 xUri->setName( sNewLibName + sScriptName.copy( nLibModuleSeparator ) );
1581 // update the new script URL
1582 _inout_rScriptCode = xUri->getUriReference();
1583 bSuccess = true;
1585 catch( const Exception& )
1587 aException = ::cppu::getCaughtException();
1588 bSuccess = false;
1591 // log the failure, if any
1592 if ( !bSuccess )
1594 m_rLogger.logRecoverable( MigrationError(
1595 ERR_SCRIPT_TRANSLATION_FAILURE,
1596 _rScriptType,
1597 _inout_rScriptCode,
1598 aException
1599 ) );
1602 return bSuccess;
1605 bool MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow( ScriptEventDescriptor& _inout_rScriptEvent ) const
1607 if ( !(_inout_rScriptEvent.ScriptType.isEmpty() || _inout_rScriptEvent.ScriptCode.isEmpty()) )
1608 return impl_adjustScriptLibrary_nothrow( _inout_rScriptEvent.ScriptType, _inout_rScriptEvent.ScriptCode );
1609 return false;
1612 bool MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow( Any& _inout_rScriptDescriptor ) const
1614 ::comphelper::NamedValueCollection aScriptDesc( _inout_rScriptDescriptor );
1616 OUString sScriptType;
1617 OUString sScript;
1620 OSL_VERIFY( aScriptDesc.get_ensureType( "EventType", sScriptType ) );
1621 OSL_VERIFY( aScriptDesc.get_ensureType( "Script", sScript ) );
1623 catch( const Exception& )
1625 m_rLogger.logRecoverable( MigrationError(
1626 ERR_INVALID_SCRIPT_DESCRIPTOR_FORMAT,
1627 ::cppu::getCaughtException()
1628 ) );
1631 if ( !(sScriptType.isEmpty() || sScript.isEmpty()) )
1632 if ( !impl_adjustScriptLibrary_nothrow( sScriptType, sScript ) )
1633 return false;
1635 aScriptDesc.put( "Script", sScript );
1636 _inout_rScriptDescriptor <<= aScriptDesc.getPropertyValues();
1637 return true;
1640 void MigrationEngine_Impl::impl_adjustDocumentEvents_nothrow( const SubDocument& _rDocument ) const
1644 Reference< XEventsSupplier > xSuppEvents( _rDocument.xDocument, UNO_QUERY );
1645 if ( !xSuppEvents.is() )
1646 // this is allowed. E.g. new-style reports currently do not support this
1647 return;
1649 Reference< XNameReplace > xEvents( xSuppEvents->getEvents(), UNO_SET_THROW );
1650 Sequence< OUString > aEventNames = xEvents->getElementNames();
1652 Any aEvent;
1653 for ( const OUString* eventName = aEventNames.getConstArray();
1654 eventName != aEventNames.getConstArray() + aEventNames.getLength();
1655 ++eventName
1658 aEvent = xEvents->getByName( *eventName );
1659 if ( !aEvent.hasValue() )
1660 continue;
1662 // translate
1663 if ( !impl_adjustScriptLibrary_nothrow( aEvent ) )
1664 continue;
1666 // put back
1667 xEvents->replaceByName( *eventName, aEvent );
1670 catch( const Exception& )
1672 m_rLogger.logRecoverable( MigrationError(
1673 ERR_ADJUSTING_DOCUMENT_EVENTS_FAILED,
1674 lcl_getSubDocumentDescription( _rDocument ),
1675 ::cppu::getCaughtException()
1676 ) );
1680 void MigrationEngine_Impl::impl_adjustDialogElementEvents_throw( const Reference< XInterface >& _rxElement ) const
1682 Reference< XScriptEventsSupplier > xEventsSupplier( _rxElement, UNO_QUERY_THROW );
1683 Reference< XNameReplace > xEvents( xEventsSupplier->getEvents(), UNO_QUERY_THROW );
1684 Sequence< OUString > aEventNames( xEvents->getElementNames() );
1686 const OUString* eventName = aEventNames.getArray();
1687 const OUString* eventNamesEnd = eventName + aEventNames.getLength();
1689 ScriptEventDescriptor aScriptEvent;
1690 for ( ; eventName != eventNamesEnd; ++eventName )
1692 OSL_VERIFY( xEvents->getByName( *eventName ) >>= aScriptEvent );
1694 if ( !impl_adjustScriptLibrary_nothrow( aScriptEvent ) )
1695 continue;
1697 xEvents->replaceByName( *eventName, makeAny( aScriptEvent ) );
1701 bool MigrationEngine_Impl::impl_adjustDialogEvents_nothrow( Any& _inout_rDialogLibraryElement,
1702 const OUString& _rDocName, const OUString& _rDialogLibName, const OUString& _rDialogName ) const
1706 // load a dialog model from the stream describing it
1707 Reference< XInputStreamProvider > xISP( _inout_rDialogLibraryElement, UNO_QUERY_THROW );
1708 Reference< XInputStream > xInput( xISP->createInputStream(), UNO_QUERY_THROW );
1710 Reference< XNameContainer > xDialogModel( m_aContext->getServiceManager()->createInstanceWithContext("com.sun.star.awt.UnoControlDialogModel", m_aContext), UNO_QUERY_THROW );
1711 ::xmlscript::importDialogModel( xInput, xDialogModel, m_aContext, m_xDocumentModel );
1713 // adjust the events of the dialog
1714 impl_adjustDialogElementEvents_throw( xDialogModel );
1716 // adjust the events of the controls
1717 Sequence< OUString > aControlNames( xDialogModel->getElementNames() );
1718 const OUString* controlName = aControlNames.getConstArray();
1719 const OUString* controlNamesEnd = controlName + aControlNames.getLength();
1720 for ( ; controlName != controlNamesEnd; ++controlName )
1722 impl_adjustDialogElementEvents_throw( Reference< XInterface >( xDialogModel->getByName( *controlName ), UNO_QUERY ) );
1725 // export dialog model
1726 xISP = ::xmlscript::exportDialogModel( xDialogModel, m_aContext, m_xDocumentModel );
1727 _inout_rDialogLibraryElement <<= xISP;
1729 catch( const Exception& )
1731 m_rLogger.logRecoverable( MigrationError(
1732 ERR_ADJUSTING_DIALOG_EVENTS_FAILED,
1733 _rDocName,
1734 _rDialogLibName,
1735 _rDialogName,
1736 ::cppu::getCaughtException()
1737 ) );
1738 return false;
1740 return true;
1743 void MigrationEngine_Impl::impl_adjustFormComponentEvents_throw( const Reference< XIndexAccess >& _rxComponentContainer ) const
1745 FormComponentIterator aCompIter( _rxComponentContainer );
1746 while ( aCompIter.hasMore() )
1748 // 1. adjust the component's scripts of the current component
1749 FormComponentScripts aComponent( aCompIter.next() );
1750 Sequence< ScriptEventDescriptor > aEvents( aComponent.getEvents() );
1752 bool bChangedComponentEvents = false;
1753 for ( ScriptEventDescriptor* scriptEvent = aEvents.getArray();
1754 scriptEvent != aEvents.getArray() + aEvents.getLength();
1755 ++scriptEvent
1758 if ( !impl_adjustScriptLibrary_nothrow( *scriptEvent ) )
1759 continue;
1761 bChangedComponentEvents = true;
1764 if ( bChangedComponentEvents )
1765 aComponent.setEvents( aEvents );
1767 // 2. step down if the component is a container itself
1768 Reference< XIndexAccess > xContainer( aComponent.getComponent(), UNO_QUERY );
1769 if ( xContainer.is() )
1770 impl_adjustFormComponentEvents_throw( xContainer );
1774 void MigrationEngine_Impl::impl_adjustFormComponentEvents_nothrow( const SubDocument& _rDocument ) const
1778 DrawPageIterator aPageIter( _rDocument.xDocument );
1779 while ( aPageIter.hasMore() )
1781 Reference< XFormsSupplier > xSuppForms( aPageIter.next(), UNO_QUERY_THROW );
1782 Reference< XIndexAccess > xForms( xSuppForms->getForms(), UNO_QUERY_THROW );
1783 impl_adjustFormComponentEvents_throw( xForms );
1786 catch( const Exception& )
1788 m_rLogger.logRecoverable( MigrationError(
1789 ERR_ADJUSTING_FORMCOMP_EVENTS_FAILED,
1790 lcl_getSubDocumentDescription( _rDocument ),
1791 ::cppu::getCaughtException()
1792 ) );
1796 bool MigrationEngine_Impl::impl_unprotectPasswordLibrary_throw( const Reference< XLibraryContainerPassword >& _rxPasswordManager,
1797 const ScriptType _eScriptType, const OUString& _rLibraryName ) const
1799 // a human-readable description of the affected library
1800 OUString sLibraryDescription(
1801 MacroMigrationResId(STR_LIBRARY_TYPE_AND_NAME).toString().
1802 replaceFirst("$type$",
1803 getScriptTypeDisplayName(_eScriptType)).
1804 replaceFirst("$library$", _rLibraryName));
1805 //TODO: probably broken if first replaceFirst can produce
1806 // fresh instance of "$library$" in subject string of second
1807 // replaceFirst
1809 InteractionHandler aHandler( m_aContext, m_xDocumentModel );
1810 OUString sPassword;
1811 while ( true )
1813 if ( !aHandler.requestDocumentPassword( sLibraryDescription, sPassword ) )
1814 // aborted by the user
1815 return false;
1817 bool bSuccessVerification = _rxPasswordManager->verifyLibraryPassword( _rLibraryName, sPassword );
1818 if ( bSuccessVerification )
1819 return true;
1824 // MigrationEngine
1825 MigrationEngine::MigrationEngine( const Reference<XComponentContext>& _rContext,
1826 const Reference< XOfficeDatabaseDocument >& _rxDocument, IMigrationProgress& _rProgress,
1827 MigrationLog& _rLogger )
1828 :m_pImpl( new MigrationEngine_Impl( _rContext, _rxDocument, _rProgress, _rLogger ) )
1832 MigrationEngine::~MigrationEngine()
1836 sal_Int32 MigrationEngine::getFormCount() const
1838 return m_pImpl->getFormCount();
1841 sal_Int32 MigrationEngine::getReportCount() const
1843 return m_pImpl->getReportCount();
1846 bool MigrationEngine::migrateAll()
1848 return m_pImpl->migrateAll();
1851 } // namespace dbmm
1853 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */