Bump version to 5.0-14
[LibreOffice.git] / dbaccess / source / ext / macromigration / migrationengine.cxx
blob91d17b2adfd40e4c681974423847f942671e6503
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/sdb/XFormDocumentsSupplier.hpp>
32 #include <com/sun/star/sdb/XReportDocumentsSupplier.hpp>
33 #include <com/sun/star/util/XCloseable.hpp>
34 #include <com/sun/star/frame/XModel.hpp>
35 #include <com/sun/star/frame/XComponentLoader.hpp>
36 #include <com/sun/star/ucb/XCommandProcessor.hpp>
37 #include <com/sun/star/ucb/XContent.hpp>
38 #include <com/sun/star/embed/XComponentSupplier.hpp>
39 #include <com/sun/star/embed/ElementModes.hpp>
40 #include <com/sun/star/document/XStorageBasedDocument.hpp>
41 #include <com/sun/star/embed/XTransactedObject.hpp>
42 #include <com/sun/star/frame/XStorable.hpp>
43 #include <com/sun/star/embed/XEmbedPersist.hpp>
44 #include <com/sun/star/script/DocumentScriptLibraryContainer.hpp>
45 #include <com/sun/star/script/DocumentDialogLibraryContainer.hpp>
46 #include <com/sun/star/document/XEmbeddedScripts.hpp>
47 #include <com/sun/star/document/XEventsSupplier.hpp>
48 #include <com/sun/star/uri/UriReferenceFactory.hpp>
49 #include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp>
50 #include <com/sun/star/form/XFormsSupplier.hpp>
51 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
52 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
53 #include <com/sun/star/script/XEventAttacherManager.hpp>
54 #include <com/sun/star/script/XLibraryContainerPassword.hpp>
55 #include <com/sun/star/io/WrongFormatException.hpp>
56 #include <com/sun/star/script/XScriptEventsSupplier.hpp>
57 #include <com/sun/star/io/XInputStreamProvider.hpp>
59 #include <comphelper/documentinfo.hxx>
60 #include <comphelper/interaction.hxx>
61 #include <comphelper/namedvaluecollection.hxx>
62 #include <comphelper/storagehelper.hxx>
63 #include <comphelper/types.hxx>
64 #include <cppuhelper/exc_hlp.hxx>
65 #include <tools/diagnose_ex.h>
66 #include <rtl/ustrbuf.hxx>
67 #include <rtl/ref.hxx>
68 #include <unotools/sharedunocomponent.hxx>
69 #include <xmlscript/xmldlg_imexp.hxx>
71 #include <vector>
72 #include <set>
73 #include <iterator>
75 #define DEFAULT_DOC_PROGRESS_RANGE 100000
77 namespace dbmm
80 using ::com::sun::star::uno::Reference;
81 using ::com::sun::star::uno::XInterface;
82 using ::com::sun::star::uno::UNO_QUERY;
83 using ::com::sun::star::uno::UNO_QUERY_THROW;
84 using ::com::sun::star::uno::UNO_SET_THROW;
85 using ::com::sun::star::uno::Exception;
86 using ::com::sun::star::uno::RuntimeException;
87 using ::com::sun::star::uno::Any;
88 using ::com::sun::star::uno::makeAny;
89 using ::com::sun::star::uno::XComponentContext;
90 using ::com::sun::star::sdb::XOfficeDatabaseDocument;
91 using ::com::sun::star::sdb::XFormDocumentsSupplier;
92 using ::com::sun::star::sdb::XReportDocumentsSupplier;
93 using ::com::sun::star::container::XNameAccess;
94 using ::com::sun::star::uno::Sequence;
95 using ::com::sun::star::util::XCloseable;
96 using ::com::sun::star::util::CloseVetoException;
97 using ::com::sun::star::lang::XComponent;
98 using ::com::sun::star::frame::XModel;
99 using ::com::sun::star::frame::XComponentLoader;
100 using ::com::sun::star::ucb::XCommandProcessor;
101 using ::com::sun::star::ucb::XContent;
102 using ::com::sun::star::ucb::Command;
103 using ::com::sun::star::embed::XComponentSupplier;
104 using ::com::sun::star::task::XStatusIndicator;
105 using ::com::sun::star::embed::XStorage;
106 using ::com::sun::star::document::XStorageBasedDocument;
107 using ::com::sun::star::embed::XTransactedObject;
108 using ::com::sun::star::frame::XStorable;
109 using ::com::sun::star::embed::XEmbedPersist;
110 using ::com::sun::star::script::DocumentDialogLibraryContainer;
111 using ::com::sun::star::script::DocumentScriptLibraryContainer;
112 using ::com::sun::star::script::XStorageBasedLibraryContainer;
113 using ::com::sun::star::document::XEmbeddedScripts;
114 using ::com::sun::star::container::XNameContainer;
115 using ::com::sun::star::document::XEventsSupplier;
116 using ::com::sun::star::container::XNameReplace;
117 using com::sun::star::uri::UriReferenceFactory;
118 using com::sun::star::uri::XUriReferenceFactory;
119 using com::sun::star::uri::XVndSunStarScriptUrlReference;
120 using ::com::sun::star::form::XFormsSupplier;
121 using ::com::sun::star::drawing::XDrawPageSupplier;
122 using ::com::sun::star::drawing::XDrawPagesSupplier;
123 using ::com::sun::star::drawing::XDrawPage;
124 using ::com::sun::star::drawing::XDrawPages;
125 using ::com::sun::star::container::XIndexAccess;
126 using ::com::sun::star::script::XEventAttacherManager;
127 using ::com::sun::star::script::ScriptEventDescriptor;
128 using ::com::sun::star::script::XLibraryContainerPassword;
129 using ::com::sun::star::io::WrongFormatException;
130 using ::com::sun::star::script::XScriptEventsSupplier;
131 using ::com::sun::star::io::XInputStreamProvider;
132 using ::com::sun::star::io::XInputStream;
134 namespace ElementModes = ::com::sun::star::embed::ElementModes;
136 // migration phases whose progresses are to be mixed into one progress
137 #define PHASE_JAVASCRIPT 1
138 #define PHASE_BEANSHELL 2
139 #define PHASE_PYTHON 3
140 #define PHASE_JAVA 4
141 #define PHASE_BASIC 5
142 #define PHASE_DIALOGS 6
144 // SubDocument
145 struct SubDocument
147 Reference< XCommandProcessor > xCommandProcessor;
148 Reference< XModel > xDocument; // valid only temporarily
149 OUString sHierarchicalName;
150 SubDocumentType eType;
151 size_t nNumber;
153 SubDocument( const Reference< XCommandProcessor >& _rxCommandProcessor, const OUString& _rName,
154 const SubDocumentType _eType, const size_t _nNumber )
155 :xCommandProcessor( _rxCommandProcessor )
156 ,xDocument()
157 ,sHierarchicalName( _rName )
158 ,eType( _eType )
159 ,nNumber( _nNumber )
164 typedef ::std::vector< SubDocument > SubDocuments;
166 // helper
167 typedef ::utl::SharedUNOComponent< XStorage > SharedStorage;
169 namespace
171 static const char sScriptsStorageName[] = "Scripts";
173 static OUString lcl_getScriptsSubStorageName( const ScriptType _eType )
175 switch ( _eType )
177 case eBeanShell: return OUString("beanshell");
178 case eJavaScript: return OUString("javascript");
179 case ePython: return OUString("python"); // TODO: is this correct?
180 case eJava: return OUString("java");
181 default:
182 break;
185 OSL_FAIL( "lcl_getScriptsSubStorageName: illegal type!" );
186 return OUString();
189 static bool lcl_getScriptTypeFromLanguage( const OUString& _rLanguage, ScriptType& _out_rScriptType )
191 struct LanguageMapping
193 const sal_Char* pAsciiLanguage;
194 const ScriptType eScriptType;
196 LanguageMapping( const sal_Char* _pAsciiLanguage, const ScriptType _eScriptType )
197 :pAsciiLanguage( _pAsciiLanguage )
198 ,eScriptType( _eScriptType )
202 aLanguageMapping[] =
204 LanguageMapping( "JavaScript", eJavaScript ),
205 LanguageMapping( "BeanShell", eBeanShell ),
206 LanguageMapping( "Java", eJava ),
207 LanguageMapping( "Python", ePython ), // TODO: is this correct?
208 LanguageMapping( "Basic", eBasic )
210 for ( size_t i=0; i < sizeof( aLanguageMapping ) / sizeof( aLanguageMapping[0] ); ++i )
212 if ( _rLanguage.equalsAscii( aLanguageMapping[i].pAsciiLanguage ) )
214 _out_rScriptType = aLanguageMapping[i].eScriptType;
215 return true;
218 OSL_FAIL( "lcl_getScriptTypeFromLanguage: unknown language!" );
219 return false;
222 OUString lcl_getSubDocumentDescription( const SubDocument& _rDocument )
224 OUString sObjectName(
225 MacroMigrationResId(
226 _rDocument.eType == eForm ? STR_FORM : STR_REPORT).toString().
227 replaceFirst("$name$", _rDocument.sHierarchicalName));
228 return sObjectName;
231 static Any lcl_executeCommand_throw( const Reference< XCommandProcessor >& _rxCommandProc,
232 const sal_Char* _pAsciiCommand )
234 OSL_PRECOND( _rxCommandProc.is(), "lcl_executeCommand_throw: illegal object!" );
235 if ( !_rxCommandProc.is() )
236 return Any();
238 Command aCommand;
239 aCommand.Name = OUString::createFromAscii( _pAsciiCommand );
240 return _rxCommandProc->execute(
241 aCommand, _rxCommandProc->createCommandIdentifier(), NULL );
244 OUString lcl_getMimeType_nothrow( const Reference< XCommandProcessor >& _rxContent )
246 OUString sMimeType;
249 Reference< XContent > xContent( _rxContent, UNO_QUERY_THROW );
250 sMimeType = xContent->getContentType();
252 catch( const Exception& )
254 DBG_UNHANDLED_EXCEPTION();
256 return sMimeType;
259 enum OpenDocResult
261 eOpenedDoc,
262 eIgnoreDoc,
263 eFailure
266 static OpenDocResult lcl_loadSubDocument_nothrow( SubDocument& _rDocument,
267 const Reference< XStatusIndicator >& _rxProgress, MigrationLog& _rLogger )
269 OSL_PRECOND( !_rDocument.xDocument.is(), "lcl_loadSubDocument_nothrow: already loaded!" );
273 ::comphelper::NamedValueCollection aLoadArgs;
274 aLoadArgs.put( "Hidden", true );
275 aLoadArgs.put( "StatusIndicator", _rxProgress );
277 Reference< XCommandProcessor > xCommandProcessor( _rDocument.xCommandProcessor, UNO_SET_THROW );
278 Command aCommand;
279 aCommand.Name = "openDesign";
280 aCommand.Argument <<= aLoadArgs.getPropertyValues();
281 Reference< XComponent > xDocComponent(
282 xCommandProcessor->execute(
283 aCommand, xCommandProcessor->createCommandIdentifier(), NULL
285 UNO_QUERY
287 OSL_ENSURE( xDocComponent.is(), "lcl_loadSubDocument_nothrow: no component loaded!" );
289 _rDocument.xDocument.set( xDocComponent, UNO_QUERY_THROW );
291 catch( const Exception& )
293 Any aError( ::cppu::getCaughtException() );
295 bool bCausedByNewStyleReport =
296 ( _rDocument.eType == eReport )
297 && ( aError.isExtractableTo( ::cppu::UnoType< WrongFormatException >::get() ) )
298 && ( lcl_getMimeType_nothrow( _rDocument.xCommandProcessor ) == "application/vnd.sun.xml.report" );
300 if ( bCausedByNewStyleReport )
302 _rLogger.logRecoverable( MigrationError(
303 ERR_NEW_STYLE_REPORT,
304 lcl_getSubDocumentDescription( _rDocument )
305 ) );
306 return eIgnoreDoc;
308 else
310 _rLogger.logFailure( MigrationError(
311 ERR_OPENING_SUB_DOCUMENT_FAILED,
312 lcl_getSubDocumentDescription( _rDocument ),
313 aError
314 ) );
317 return _rDocument.xDocument.is() ? eOpenedDoc : eFailure;
320 static bool lcl_unloadSubDocument_nothrow( SubDocument& _rDocument, MigrationLog& _rLogger )
322 bool bSuccess = false;
323 Any aException;
326 OSL_VERIFY( lcl_executeCommand_throw( _rDocument.xCommandProcessor, "close" ) >>= bSuccess );
328 catch( const Exception& )
330 aException = ::cppu::getCaughtException();
333 // log the failure, if any
334 if ( !bSuccess )
336 _rLogger.logFailure( MigrationError(
337 ERR_CLOSING_SUB_DOCUMENT_FAILED,
338 lcl_getSubDocumentDescription( _rDocument ),
339 aException
340 ) );
343 _rDocument.xDocument.clear();
344 return bSuccess;
347 bool lcl_commitStorage_nothrow( const Reference< XStorage >& _rxStorage )
351 Reference< XTransactedObject > xTrans( _rxStorage, UNO_QUERY_THROW );
352 xTrans->commit();
354 catch( const Exception& )
356 return false;
358 return true;
361 bool lcl_commitDocumentStorage_nothrow( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger )
363 bool bSuccess = false;
364 Any aException;
367 Reference< XStorageBasedDocument > xStorageDoc( _rxDocument, UNO_QUERY_THROW );
368 Reference< XStorage > xDocStorage( xStorageDoc->getDocumentStorage(), UNO_QUERY_THROW );
369 bSuccess = lcl_commitStorage_nothrow( xDocStorage );
371 catch( const Exception& )
373 aException = ::cppu::getCaughtException();
376 // log the failure, if any
377 if ( !bSuccess )
379 _rLogger.logFailure( MigrationError(
380 ERR_STORAGE_COMMIT_FAILED,
381 ::comphelper::DocumentInfo::getDocumentTitle( _rxDocument ),
382 aException
383 ) );
385 return bSuccess;
388 bool lcl_storeDocument_nothrow( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger )
390 bool bSuccess = false;
391 Any aException;
394 Reference< XStorable > xStorable( _rxDocument, UNO_QUERY_THROW );
395 xStorable->store();
396 bSuccess = true;
398 catch( const Exception& )
400 aException = ::cppu::getCaughtException();
403 // log the failure, if any
404 if ( !bSuccess )
406 _rLogger.logFailure( MigrationError(
407 ERR_STORING_DATABASEDOC_FAILED,
408 aException
409 ) );
411 return bSuccess;
414 bool lcl_storeEmbeddedDocument_nothrow( const SubDocument& _rDocument )
418 lcl_executeCommand_throw( _rDocument.xCommandProcessor, "store" );
420 catch( const Exception& )
422 DBG_UNHANDLED_EXCEPTION();
423 return false;
425 return true;
429 // DrawPageIterator
430 class DrawPageIterator
432 public:
433 DrawPageIterator( const Reference< XModel >& _rxDocument )
434 :m_xDocument( _rxDocument )
435 ,m_nPageCount( 0 )
436 ,m_nCurrentPage( 0 )
438 Reference< XDrawPageSupplier > xSingle( _rxDocument, UNO_QUERY );
439 Reference< XDrawPagesSupplier > xMulti( _rxDocument, UNO_QUERY );
440 if ( xSingle.is() )
442 m_xSinglePage.set( xSingle->getDrawPage(), UNO_SET_THROW );
443 m_nPageCount = 1;
445 else if ( xMulti.is() )
447 m_xMultiPages.set( xMulti->getDrawPages(), UNO_SET_THROW );
448 m_nPageCount = m_xMultiPages->getCount();
452 bool hasMore() const
454 return m_nCurrentPage < m_nPageCount;
457 Reference< XDrawPage > next()
459 Reference< XDrawPage > xNextPage;
461 if ( m_xSinglePage.is() )
463 xNextPage = m_xSinglePage;
465 else if ( m_xMultiPages.is() )
467 xNextPage.set( m_xMultiPages->getByIndex( m_nCurrentPage ), UNO_QUERY_THROW );
469 ++m_nCurrentPage;
470 return xNextPage;
473 private:
474 const Reference< XModel > m_xDocument;
475 Reference< XDrawPage > m_xSinglePage;
476 Reference< XDrawPages > m_xMultiPages;
477 sal_Int32 m_nPageCount;
478 sal_Int32 m_nCurrentPage;
481 // FormComponentScripts
482 class FormComponentScripts
484 public:
485 FormComponentScripts(
486 const Reference< XInterface >& _rxComponent,
487 const Reference< XEventAttacherManager >& _rxManager,
488 const sal_Int32 _nIndex
490 :m_xComponent( _rxComponent )
491 ,m_xManager( _rxManager )
492 ,m_nIndex( _nIndex )
496 Sequence< ScriptEventDescriptor > getEvents() const
498 return m_xManager->getScriptEvents( m_nIndex );
501 void setEvents( const Sequence< ScriptEventDescriptor >& _rEvents ) const
503 m_xManager->registerScriptEvents( m_nIndex, _rEvents );
506 const Reference< XInterface >& getComponent() const
508 return m_xComponent;
511 private:
512 const Reference< XInterface > m_xComponent;
513 const Reference< XEventAttacherManager > m_xManager;
514 const sal_Int32 m_nIndex;
517 // FormComponentIterator
518 class FormComponentIterator
520 public:
521 FormComponentIterator( const Reference< XIndexAccess >& _rxContainer )
522 :m_xContainer( _rxContainer )
523 ,m_xEventManager( _rxContainer, UNO_QUERY_THROW )
524 ,m_nElementCount( _rxContainer->getCount() )
525 ,m_nCurrentElement( 0 )
529 bool hasMore() const
531 return m_nCurrentElement < m_nElementCount;
534 FormComponentScripts next()
536 FormComponentScripts aComponent(
537 Reference< XInterface >( m_xContainer->getByIndex( m_nCurrentElement ), UNO_QUERY_THROW ),
538 m_xEventManager,
539 m_nCurrentElement
541 ++m_nCurrentElement;
542 return aComponent;
545 private:
546 const Reference< XIndexAccess > m_xContainer;
547 const Reference< XEventAttacherManager > m_xEventManager;
548 const sal_Int32 m_nElementCount;
549 sal_Int32 m_nCurrentElement;
553 // ScriptsStorage - declaration
554 /** a helper class which encapsulates access to the storages for Java/Script, BeanShell, and Python scripts,
555 i.e. all script types which can be manipulated on storage level.
557 class ScriptsStorage
559 public:
560 ScriptsStorage( MigrationLog& _rLogger );
561 ScriptsStorage( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger );
562 ~ScriptsStorage();
564 /** determines whether the instance is valid, i.e. refers to a valid root storage
565 for reading/storing scripts
567 inline bool isValid() const { return m_xScriptsStorage.is(); }
569 /** binds the instance to a new document. Only to be called when the instance is not yet
570 bound (i.e. isValid returns <FALSE/>).
572 void bind( const Reference< XModel >& _rxDocument );
574 /// determines whether scripts of the given type are present
575 bool hasScripts( const ScriptType _eType ) const;
577 /// returns the root storage for the scripts of the given type
578 SharedStorage
579 getScriptsRoot( const ScriptType _eType ) const;
581 /** returns the names of the elements in the "Scripts" storage
583 ::std::set< OUString >
584 getElementNames() const;
586 /** removes the sub storage for a given script type
587 @precond
588 the respective storage is empty
589 @precond
590 the ScriptsStorage instance was opened for writing
592 void removeScriptTypeStorage( const ScriptType _eType ) const;
594 /** commits the changes at our XStorage object
596 bool commit();
598 /** removes the "Scripts" sub storage from the given document's root storage
599 @precond
600 the "Scripts" storage is empty
602 static bool
603 removeFromDocument( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger );
605 private:
606 MigrationLog& m_rLogger;
607 SharedStorage m_xScriptsStorage;
610 // ScriptsStorage - implementation
611 ScriptsStorage::ScriptsStorage( MigrationLog& _rLogger )
612 :m_rLogger( _rLogger )
613 ,m_xScriptsStorage()
617 ScriptsStorage::ScriptsStorage( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger )
618 :m_rLogger( _rLogger )
619 ,m_xScriptsStorage()
621 bind( _rxDocument );
624 ScriptsStorage::~ScriptsStorage()
628 bool ScriptsStorage::commit()
630 return lcl_commitStorage_nothrow( m_xScriptsStorage );
633 void ScriptsStorage::bind( const Reference< XModel >& _rxDocument )
635 OSL_PRECOND( !isValid(), "ScriptsStorage:bind: did not bother, yet, to check whether this is allowed!" );
638 Reference< XStorageBasedDocument > xStorageDoc( _rxDocument, UNO_QUERY_THROW );
639 Reference< XStorage > xDocStorage( xStorageDoc->getDocumentStorage(), UNO_QUERY_THROW );
641 // the "Scripts" storage exist, or if it does not (yet) exist and we are in write mode
642 // => open the storage
643 if ( ( xDocStorage->hasByName( sScriptsStorageName )
644 && xDocStorage->isStorageElement( sScriptsStorageName )
646 || !xDocStorage->hasByName( sScriptsStorageName )
649 m_xScriptsStorage.set(
650 xDocStorage->openStorageElement(
651 sScriptsStorageName, ElementModes::READWRITE
653 UNO_QUERY_THROW
657 catch( const Exception& )
659 m_rLogger.logFailure( MigrationError(
660 ERR_BIND_SCRIPT_STORAGE_FAILED,
661 ::comphelper::DocumentInfo::getDocumentTitle( _rxDocument ),
662 ::cppu::getCaughtException()
663 ) );
667 bool ScriptsStorage::hasScripts( const ScriptType _eType ) const
669 OSL_PRECOND( isValid(), "ScriptsStorage::hasScripts: illegal call!" );
670 if ( !isValid() )
671 return false;
673 const OUString& rSubStorageName( lcl_getScriptsSubStorageName( _eType ) );
674 return m_xScriptsStorage->hasByName( rSubStorageName )
675 && m_xScriptsStorage->isStorageElement( rSubStorageName );
678 SharedStorage ScriptsStorage::getScriptsRoot( const ScriptType _eType ) const
680 SharedStorage xStorage;
681 if ( isValid() )
683 xStorage.reset( m_xScriptsStorage->openStorageElement(
684 lcl_getScriptsSubStorageName( _eType ), ElementModes::READWRITE
685 ) );
687 return xStorage;
690 ::std::set< OUString > ScriptsStorage::getElementNames() const
692 Sequence< OUString > aElementNames;
693 if ( isValid() )
694 aElementNames = m_xScriptsStorage->getElementNames();
696 ::std::set< OUString > aNames;
697 ::std::copy(
698 aElementNames.getConstArray(),
699 aElementNames.getConstArray() + aElementNames.getLength(),
700 ::std::insert_iterator< ::std::set< OUString > >( aNames, aNames.end() )
702 return aNames;
705 void ScriptsStorage::removeScriptTypeStorage( const ScriptType _eType ) const
707 OUString sSubStorageName( lcl_getScriptsSubStorageName( _eType ) );
708 if ( m_xScriptsStorage->hasByName( sSubStorageName ) )
709 m_xScriptsStorage->removeElement( sSubStorageName );
712 bool ScriptsStorage::removeFromDocument( const Reference< XModel >& _rxDocument, MigrationLog& _rLogger )
716 Reference< XStorageBasedDocument > xStorageDoc( _rxDocument, UNO_QUERY_THROW );
717 Reference< XStorage > xDocStorage( xStorageDoc->getDocumentStorage(), UNO_QUERY_THROW );
718 xDocStorage->removeElement( sScriptsStorageName );
720 catch( const Exception& )
722 _rLogger.logFailure( MigrationError(
723 ERR_REMOVE_SCRIPTS_STORAGE_FAILED,
724 ::comphelper::DocumentInfo::getDocumentTitle( _rxDocument ),
725 ::cppu::getCaughtException()
726 ) ) ;
727 return false;
729 return true;
732 // ProgressDelegator
733 class ProgressDelegator : public IProgressConsumer
735 public:
736 ProgressDelegator( IMigrationProgress& _rDelegator,
737 const OUString& _rObjectName,
738 const OUString& _rAction
740 :m_rDelegator( _rDelegator )
741 ,m_sObjectName( _rObjectName )
742 ,m_sAction( _rAction )
745 virtual ~ProgressDelegator()
749 // IProgressConsumer
750 virtual void start( sal_uInt32 _nRange ) SAL_OVERRIDE
752 m_rDelegator.startObject( m_sObjectName, m_sAction, _nRange );
754 virtual void advance( sal_uInt32 _nValue ) SAL_OVERRIDE
756 m_rDelegator.setObjectProgressValue( _nValue );
758 virtual void end() SAL_OVERRIDE
760 m_rDelegator.endObject();
763 private:
764 IMigrationProgress& m_rDelegator;
765 OUString m_sObjectName;
766 OUString m_sAction;
769 // PhaseGuard
770 class PhaseGuard
772 public:
773 PhaseGuard( ProgressMixer& _rMixer )
774 :m_rMixer( _rMixer )
778 ~PhaseGuard()
780 m_rMixer.endPhase();
783 void start( const PhaseID _nID, const sal_uInt32 _nPhaseRange )
785 m_rMixer.startPhase( _nID, _nPhaseRange );
788 private:
789 ProgressMixer& m_rMixer;
792 // MigrationEngine_Impl - declaration
793 class MigrationEngine_Impl
795 public:
796 MigrationEngine_Impl(
797 const Reference<XComponentContext>& _rContext,
798 const Reference< XOfficeDatabaseDocument >& _rxDocument,
799 IMigrationProgress& _rProgress,
800 MigrationLog& _rLogger
802 ~MigrationEngine_Impl();
804 inline size_t getFormCount() const { return m_nFormCount; }
805 inline size_t getReportCount()const { return m_nReportCount; }
806 bool migrateAll();
808 private:
809 Reference<XComponentContext> m_aContext;
810 const Reference< XOfficeDatabaseDocument > m_xDocument;
811 const Reference< XModel > m_xDocumentModel;
812 IMigrationProgress& m_rProgress;
813 MigrationLog& m_rLogger;
814 mutable DocumentID m_nCurrentDocumentID;
815 SubDocuments m_aSubDocs;
816 size_t m_nFormCount;
817 size_t m_nReportCount;
819 private:
820 /** collects a description of all sub documents of our database document
822 @return
823 <TRUE/> if and only if collecting the documents was successful
825 bool impl_collectSubDocuments_nothrow();
827 /** migrates the macros/scripts of the given sub document
829 bool impl_handleDocument_nothrow( const SubDocument& _rDocument ) const;
831 /** checks the structure of the 'Scripts' folder of a sub document
832 for unknown elements
834 @return
835 <TRUE/> if and only if the 'Scripts' folder contains known elements only.
837 bool impl_checkScriptStorageStructure_nothrow( const SubDocument& _rDocument ) const;
839 /** migrates the scripts of the given "storage-based" script type
841 bool impl_migrateScriptStorage_nothrow(
842 const SubDocument& _rDocument,
843 const ScriptType _eScriptType,
844 ProgressMixer& _rProgress,
845 const PhaseID _nPhaseID
846 ) const;
848 /** migrates the content of the given "container based" libraries (Basic/Dialogs)
850 bool impl_migrateContainerLibraries_nothrow(
851 const SubDocument& _rDocument,
852 const ScriptType _eScriptType,
853 ProgressMixer& _rProgress,
854 const PhaseID _nPhaseID
855 ) const;
857 /** adjusts the events for the given dialog/element, taking into account the new names
858 of the moved libraries
860 void impl_adjustDialogElementEvents_throw(
861 const Reference< XInterface >& _rxElement
862 ) const;
864 /** adjusts the events in the given dialog, and its controls, taking into account the new names
865 of the moved libraries
867 bool impl_adjustDialogEvents_nothrow(
868 Any& _inout_rDialogLibraryElement,
869 const OUString& _rDocName,
870 const OUString& _rDialogLibName,
871 const OUString& _rDialogName
872 ) const;
874 /** adjust the document-events which refer to macros/scripts in the document, taking into
875 account the new names of the moved libraries
877 bool impl_adjustDocumentEvents_nothrow(
878 const SubDocument& _rDocument
879 ) const;
881 /** adjusts the script references bound to form component events
883 bool impl_adjustFormComponentEvents_nothrow(
884 const SubDocument& _rDocument
885 ) const;
887 /** adjusts the script references for the elements of the given form component container
889 void impl_adjustFormComponentEvents_throw(
890 const Reference< XIndexAccess >& _rxComponentContainer
891 ) const;
893 /** adjusts the library name in the given script URL, so that it reflects
894 the new name of the library
896 @return <TRUE/>
897 if and only if adjustments to the script code have been made
899 bool impl_adjustScriptLibrary_nothrow(
900 const OUString& _rScriptType,
901 OUString& _inout_rScriptCode
902 ) const;
904 bool impl_adjustScriptLibrary_nothrow( Any& _inout_rScriptDescriptor ) const;
905 bool impl_adjustScriptLibrary_nothrow( ScriptEventDescriptor& _inout_rScriptEvent ) const;
907 /** asks the user for a password for the given library, and unprotects the library
909 @return <TRUE/>
910 if and only if the library could be successfully unprotected
912 bool impl_unprotectPasswordLibrary_throw(
913 const Reference< XLibraryContainerPassword >& _rxPasswordManager,
914 const ScriptType _eScriptType,
915 const OUString& _rLibraryName
916 ) const;
919 // MigrationEngine_Impl - implementation
920 MigrationEngine_Impl::MigrationEngine_Impl( const Reference<XComponentContext>& _rContext,
921 const Reference< XOfficeDatabaseDocument >& _rxDocument, IMigrationProgress& _rProgress, MigrationLog& _rLogger )
922 :m_aContext( _rContext )
923 ,m_xDocument( _rxDocument )
924 ,m_xDocumentModel( _rxDocument, UNO_QUERY_THROW )
925 ,m_rProgress( _rProgress )
926 ,m_rLogger( _rLogger )
927 ,m_nCurrentDocumentID( - 1 )
928 ,m_aSubDocs()
929 ,m_nFormCount( 0 )
930 ,m_nReportCount( 0 )
932 OSL_VERIFY( impl_collectSubDocuments_nothrow() );
935 MigrationEngine_Impl::~MigrationEngine_Impl()
939 bool MigrationEngine_Impl::migrateAll()
941 if ( m_aSubDocs.empty() )
943 OSL_FAIL( "MigrationEngine_Impl::migrateAll: no forms/reports found!" );
944 // The whole migration wizard is not expected to be called when there are no forms/reports
945 // with macros, not to mention when there are no forms/reports at all.
946 return false;
949 // initialize global progress
950 sal_Int32 nOverallRange( m_aSubDocs.size() );
951 OUString sProgressSkeleton(
952 MacroMigrationResId( STR_OVERALL_PROGRESS).toString().
953 replaceFirst("$overall$", OUString::number(nOverallRange)));
955 m_rProgress.start( nOverallRange );
957 for ( SubDocuments::const_iterator doc = m_aSubDocs.begin();
958 doc != m_aSubDocs.end();
959 ++doc
962 sal_Int32 nOverallProgressValue( doc - m_aSubDocs.begin() + 1 );
963 // update overall progress text
964 OUString sOverallProgress(
965 sProgressSkeleton.replaceFirst("$current$",
966 OUString::number(nOverallProgressValue)));
967 m_rProgress.setOverallProgressText( sOverallProgress );
969 // migrate document
970 if ( !impl_handleDocument_nothrow( *doc ) )
971 return false;
973 // update overall progress vallue
974 m_rProgress.setOverallProgressValue( nOverallProgressValue );
977 // commit the root storage of the database document, for all changes made so far to take effect
978 if ( !lcl_commitDocumentStorage_nothrow( m_xDocumentModel, m_rLogger ) )
979 return false;
981 // save the document
982 if ( !lcl_storeDocument_nothrow( m_xDocumentModel, m_rLogger ) )
983 return false;
985 return true;
988 namespace
990 void lcl_collectHierarchicalElementNames_throw(
991 const Reference< XNameAccess >& _rxContainer, const OUString& _rContainerLoc,
992 SubDocuments& _out_rDocs, const SubDocumentType _eType, size_t& _io_counter )
994 const OUString sHierarhicalBase(
995 _rContainerLoc.isEmpty() ? OUString() :
996 OUStringBuffer( _rContainerLoc ).appendAscii( "/" ).makeStringAndClear());
998 Sequence< OUString > aElementNames( _rxContainer->getElementNames() );
999 for ( const OUString* elementName = aElementNames.getConstArray();
1000 elementName != aElementNames.getConstArray() + aElementNames.getLength();
1001 ++elementName
1004 Any aElement( _rxContainer->getByName( *elementName ) );
1005 OUString sElementName( sHierarhicalBase + *elementName );
1007 Reference< XNameAccess > xSubContainer( aElement, UNO_QUERY );
1008 if ( xSubContainer.is() )
1010 lcl_collectHierarchicalElementNames_throw( xSubContainer, sElementName, _out_rDocs, _eType, _io_counter );
1012 else
1014 Reference< XCommandProcessor > xCommandProcessor( aElement, UNO_QUERY );
1015 OSL_ENSURE( xCommandProcessor.is(), "lcl_collectHierarchicalElementNames_throw: no container, and no command processor? What *is* it, then?!" );
1016 if ( xCommandProcessor.is() )
1018 _out_rDocs.push_back( SubDocument( xCommandProcessor, sElementName, _eType, ++_io_counter ) );
1025 bool MigrationEngine_Impl::impl_collectSubDocuments_nothrow()
1027 OSL_PRECOND( m_xDocument.is(), "MigrationEngine_Impl::impl_collectSubDocuments_nothrow: invalid document!" );
1028 if ( !m_xDocument.is() )
1029 return false;
1033 Reference< XNameAccess > xDocContainer( m_xDocument->getFormDocuments(), UNO_SET_THROW );
1034 m_nFormCount = 0;
1035 lcl_collectHierarchicalElementNames_throw( xDocContainer, OUString(), m_aSubDocs, eForm, m_nFormCount );
1037 xDocContainer.set( m_xDocument->getReportDocuments(), UNO_SET_THROW );
1038 m_nReportCount = 0;
1039 lcl_collectHierarchicalElementNames_throw( xDocContainer, OUString(), m_aSubDocs, eReport, m_nReportCount );
1041 catch( const Exception& )
1043 m_rLogger.logFailure( MigrationError(
1044 ERR_COLLECTING_DOCUMENTS_FAILED,
1045 ::cppu::getCaughtException()
1046 ) );
1047 return false;
1049 return true;
1052 bool MigrationEngine_Impl::impl_handleDocument_nothrow( const SubDocument& _rDocument ) const
1054 OSL_ENSURE( m_nCurrentDocumentID == -1,
1055 "MigrationEngine_Impl::impl_handleDocument_nothrow: there already is a current document!");
1056 m_nCurrentDocumentID = m_rLogger.startedDocument( _rDocument.eType, _rDocument.sHierarchicalName );
1058 // start the progress
1059 OUString sObjectName( lcl_getSubDocumentDescription( _rDocument ) );
1060 m_rProgress.startObject( sObjectName, OUString(), DEFAULT_DOC_PROGRESS_RANGE );
1062 // load the document
1063 Reference< ProgressCapture > pStatusIndicator( new ProgressCapture( sObjectName, m_rProgress ) );
1064 SubDocument aSubDocument( _rDocument );
1065 OpenDocResult eResult = lcl_loadSubDocument_nothrow( aSubDocument, pStatusIndicator.get(), m_rLogger );
1066 if ( eResult != eOpenedDoc )
1068 pStatusIndicator->dispose();
1069 m_rProgress.endObject();
1070 m_rLogger.finishedDocument( m_nCurrentDocumentID );
1071 m_nCurrentDocumentID = -1;
1072 return ( eResult == eIgnoreDoc );
1075 // migrate the libraries
1076 ProgressDelegator aDelegator(m_rProgress, sObjectName, MacroMigrationResId(STR_MIGRATING_LIBS).toString());
1077 ProgressMixer aProgressMixer( aDelegator );
1078 aProgressMixer.registerPhase( PHASE_JAVASCRIPT, 1 );
1079 aProgressMixer.registerPhase( PHASE_BEANSHELL, 1 );
1080 aProgressMixer.registerPhase( PHASE_PYTHON, 1 );
1081 aProgressMixer.registerPhase( PHASE_JAVA, 1 );
1082 aProgressMixer.registerPhase( PHASE_BASIC, 5 );
1083 // more weight than the others, assuming that usually, there are many more Basic macros than any other scripts
1084 aProgressMixer.registerPhase( PHASE_DIALOGS, 1 );
1086 bool bSuccess = impl_checkScriptStorageStructure_nothrow( aSubDocument );
1088 // migrate storage-based script libraries (which can be handled by mere storage operations)
1089 bSuccess = bSuccess
1090 && impl_migrateScriptStorage_nothrow( aSubDocument, eJavaScript, aProgressMixer, PHASE_JAVASCRIPT )
1091 && impl_migrateScriptStorage_nothrow( aSubDocument, eBeanShell, aProgressMixer, PHASE_BEANSHELL )
1092 && impl_migrateScriptStorage_nothrow( aSubDocument, ePython, aProgressMixer, PHASE_PYTHON )
1093 && impl_migrateScriptStorage_nothrow( aSubDocument, eJava, aProgressMixer, PHASE_JAVA );
1095 // migrate Basic and dialog libraries
1096 bSuccess = bSuccess
1097 && impl_migrateContainerLibraries_nothrow( aSubDocument, eBasic, aProgressMixer, PHASE_BASIC )
1098 && impl_migrateContainerLibraries_nothrow( aSubDocument, eDialog, aProgressMixer, PHASE_DIALOGS );
1099 // order matters: First Basic scripts, then dialogs. So we can adjust references from the latter
1100 // to the former
1102 // adjust the events in the document
1103 // (note that errors are ignored here - failure to convert a script reference
1104 // is not considered a critical error)
1105 if ( bSuccess )
1107 impl_adjustDocumentEvents_nothrow( aSubDocument );
1108 impl_adjustFormComponentEvents_nothrow( aSubDocument );
1111 // clean up
1112 // store the sub document, including removal of the (now obsolete) "Scripts" sub folder
1113 if ( m_rLogger.movedAnyLibrary( m_nCurrentDocumentID ) )
1115 bSuccess = bSuccess
1116 && ScriptsStorage::removeFromDocument( aSubDocument.xDocument, m_rLogger )
1117 && lcl_commitDocumentStorage_nothrow( aSubDocument.xDocument, m_rLogger )
1118 && lcl_storeEmbeddedDocument_nothrow( aSubDocument );
1121 // unload in any case, even if we were not successful
1122 bSuccess = lcl_unloadSubDocument_nothrow( aSubDocument, m_rLogger )
1123 && bSuccess;
1125 pStatusIndicator->dispose();
1127 // end the progress, just in case the ProgressCapture didn't receive the XStatusIndicator::end event
1128 m_rProgress.endObject();
1130 m_rLogger.finishedDocument( m_nCurrentDocumentID );
1131 m_nCurrentDocumentID = -1;
1132 return bSuccess;
1135 namespace
1137 static OUString lcl_createTargetLibName( const SubDocument& _rDocument,
1138 const OUString& _rSourceLibName, const Reference< XNameAccess >& _rxTargetContainer )
1140 // The new library name is composed from the prefix, the base name, and the old library name.
1141 const OUString sPrefix = (_rDocument.eType == eForm)?OUString("Form_"): OUString("Report_");
1143 OUString sBaseName( _rDocument.sHierarchicalName.copy(
1144 _rDocument.sHierarchicalName.lastIndexOf( '/' ) + 1 ) );
1145 // Normalize this name. In our current storage implementation (and script containers in a document
1146 // are finally mapped to sub storages of the document storage), not all characters are allowed.
1147 // The bug requesting to change this is #i95409#.
1148 // Unfortunately, the storage implementation does not complain if you use invalid characters/names, but instead
1149 // it silently accepts them, and produces garbage in the file (#i95408).
1150 // So, until especially the former is fixed, we need to strip all invalid characters from the name.
1151 // #i95865#
1153 // The general idea is to replace invalid characters with '_'. However, since "valid" essentially means
1154 // ASCII only, this implies that for a lot of languages, we would simply replace everything with '_',
1155 // which of course is not desired.
1156 // So, we use a heuristics: If the name contains at most 3 invalid characters, and as many valid as invalid
1157 // characters, then we use the replacement. Otherwise, we just use a unambiguous number for the sub document.
1158 sal_Int32 nValid=0, nInvalid=0;
1159 const sal_Unicode* pBaseName = sBaseName.getStr();
1160 const sal_Int32 nBaseNameLen = sBaseName.getLength();
1161 for ( sal_Int32 i=0; i<nBaseNameLen; ++i )
1163 if ( ::comphelper::OStorageHelper::IsValidZipEntryFileName( pBaseName + i, 1, false ) )
1164 ++nValid;
1165 else
1166 ++nInvalid;
1168 if ( ( nInvalid <= 3 ) && ( nInvalid * 2 <= nValid ) )
1169 { // not "too many" invalid => replace them
1170 OUStringBuffer aReplacement;
1171 aReplacement.ensureCapacity( nBaseNameLen );
1172 aReplacement.append( sBaseName );
1173 const sal_Unicode* pReplacement = aReplacement.getStr();
1174 for ( sal_Int32 i=0; i<nBaseNameLen; ++i )
1176 if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( pReplacement + i, 1, false ) )
1177 aReplacement[i] = '_';
1179 sBaseName = aReplacement.makeStringAndClear();
1181 OUString sTargetName( sPrefix + sBaseName + "_" + _rSourceLibName );
1182 if ( !_rxTargetContainer->hasByName( sTargetName ) )
1183 return sTargetName;
1186 // "too many" invalid characters, or the name composed with the base name was already used.
1187 // (The latter is valid, since there can be multiple sub documents with the same base name,
1188 // in different levels in the hierarchy.)
1189 // In this case, just use the umambiguous sub document number.
1190 return sPrefix + OUString::number( _rDocument.nNumber ) + "_" + _rSourceLibName;
1194 bool MigrationEngine_Impl::impl_checkScriptStorageStructure_nothrow( const SubDocument& _rDocument ) const
1196 OSL_PRECOND( _rDocument.xDocument.is(), "MigrationEngine_Impl::impl_checkScriptStorageStructure_nothrow: invalid document!" );
1197 if ( !_rDocument.xDocument.is() )
1198 return false;
1202 // the root storage of the document whose scripts are to be migrated
1203 ScriptsStorage aDocStorage( _rDocument.xDocument, m_rLogger );
1204 if ( !aDocStorage.isValid() )
1205 { // no scripts at all, or no scripts of the given type
1206 return !m_rLogger.hadFailure();
1208 ::std::set< OUString > aElementNames( aDocStorage.getElementNames() );
1210 ScriptType aKnownStorageBasedTypes[] = {
1211 eBeanShell, eJavaScript, ePython, eJava
1213 for ( size_t i=0; i<sizeof( aKnownStorageBasedTypes ) / sizeof( aKnownStorageBasedTypes[0] ); ++i )
1214 aElementNames.erase( lcl_getScriptsSubStorageName( aKnownStorageBasedTypes[i] ) );
1216 if ( !aElementNames.empty() )
1218 m_rLogger.logFailure( MigrationError(
1219 ERR_UNKNOWN_SCRIPT_FOLDER,
1220 lcl_getSubDocumentDescription( _rDocument ),
1221 *aElementNames.begin()
1222 ) );
1223 return false;
1226 catch( const Exception& )
1228 m_rLogger.logFailure( MigrationError(
1229 ERR_EXAMINING_SCRIPTS_FOLDER_FAILED,
1230 lcl_getSubDocumentDescription( _rDocument ),
1231 ::cppu::getCaughtException()
1232 ) );
1233 return false;
1235 return true;
1238 bool MigrationEngine_Impl::impl_migrateScriptStorage_nothrow( const SubDocument& _rDocument,
1239 const ScriptType _eScriptType, ProgressMixer& _rProgress, const PhaseID _nPhaseID ) const
1241 OSL_PRECOND( _rDocument.xDocument.is(), "MigrationEngine_Impl::impl_migrateScriptStorage_nothrow: invalid document!" );
1242 if ( !_rDocument.xDocument.is() )
1243 return false;
1245 ScriptsStorage aDatabaseScripts( m_rLogger );
1246 // the scripts of our complete database document - created on demand only
1247 SharedStorage xTargetStorage;
1248 // the target for moving the scripts storages - created on demand only
1250 PhaseGuard aPhase( _rProgress );
1251 bool bSuccess = false;
1252 Any aException;
1255 // the root storage of the document whose scripts are to be migrated
1256 ScriptsStorage aDocStorage( _rDocument.xDocument, m_rLogger );
1257 if ( !aDocStorage.isValid()
1258 || !aDocStorage.hasScripts( _eScriptType )
1261 // no scripts at all, or no scripts of the given type
1262 _rProgress.startPhase( _nPhaseID, 1 );
1263 _rProgress.endPhase();
1264 return !m_rLogger.hadFailure();
1267 SharedStorage xScriptsRoot( aDocStorage.getScriptsRoot( _eScriptType ) );
1268 if ( !xScriptsRoot.is() )
1269 throw RuntimeException("internal error");
1271 // loop through the script libraries
1272 Sequence< OUString > aStorageElements( xScriptsRoot->getElementNames() );
1273 aPhase.start( _nPhaseID, aStorageElements.getLength() );
1275 for ( const OUString* element = aStorageElements.getConstArray();
1276 element != aStorageElements.getConstArray() + aStorageElements.getLength();
1277 ++element
1280 bool bIsScriptLibrary = xScriptsRoot->isStorageElement( *element );
1281 OSL_ENSURE( bIsScriptLibrary,
1282 "MigrationEngine_Impl::impl_migrateScriptStorage_nothrow: warning: unknown scripts storage structure!" );
1283 // we cannot handle this. We would need to copy this stream to the respective scripts storage
1284 // of the database document, but we cannot guarantee that the name is not used, yet, and we cannot
1285 // simply rename the thing.
1286 if ( !bIsScriptLibrary )
1288 m_rLogger.logFailure( MigrationError(
1289 ERR_UNEXPECTED_LIBSTORAGE_ELEMENT,
1290 lcl_getSubDocumentDescription( _rDocument ),
1291 getScriptTypeDisplayName( _eScriptType ),
1292 *element
1293 ) );
1294 return false;
1297 // ensure we have access to the DBDoc's scripts storage
1298 if ( !aDatabaseScripts.isValid() )
1299 { // not needed 'til now
1300 aDatabaseScripts.bind( m_xDocumentModel );
1301 if ( aDatabaseScripts.isValid() )
1302 xTargetStorage = aDatabaseScripts.getScriptsRoot( _eScriptType );
1304 if ( !xTargetStorage.is() )
1306 m_rLogger.logFailure( MigrationError(
1307 ERR_CREATING_DBDOC_SCRIPT_STORAGE_FAILED,
1308 getScriptTypeDisplayName( _eScriptType )
1309 ) );
1310 return false;
1314 // move the library to the DBDoc's scripts library, under the new name
1315 OUString sNewLibName( lcl_createTargetLibName( _rDocument, *element, xTargetStorage.getTyped().get() ) );
1316 xScriptsRoot->moveElementTo( *element, xTargetStorage, sNewLibName );
1318 // log the fact that we moved the library
1319 m_rLogger.movedLibrary( m_nCurrentDocumentID, _eScriptType, *element, sNewLibName );
1321 // progress
1322 _rProgress.advancePhase( element - aStorageElements.getConstArray() );
1325 // commit the storages, so the changes we made persist
1326 if ( !lcl_commitStorage_nothrow( xScriptsRoot )
1327 || ( xTargetStorage.is() && !lcl_commitStorage_nothrow( xTargetStorage ) )
1330 m_rLogger.logFailure( MigrationError(
1331 ERR_COMMITTING_SCRIPT_STORAGES_FAILED,
1332 getScriptTypeDisplayName( _eScriptType ),
1333 lcl_getSubDocumentDescription( _rDocument )
1334 ) );
1335 return false;
1338 // now that the concrete scripts storage does not have any elements anymore,
1339 // remove it
1340 xScriptsRoot.reset( NULL ); // need to reset the storage to be allowed to remove it
1341 aDocStorage.removeScriptTypeStorage( _eScriptType );
1343 // done so far
1344 bSuccess = aDocStorage.commit()
1345 && aDatabaseScripts.commit();
1347 catch( const Exception& )
1349 aException = ::cppu::getCaughtException();
1350 bSuccess = false;
1353 // log the error, if any
1354 if ( !bSuccess )
1356 m_rLogger.logFailure( MigrationError(
1357 ERR_GENERAL_SCRIPT_MIGRATION_FAILURE,
1358 getScriptTypeDisplayName( _eScriptType ),
1359 lcl_getSubDocumentDescription( _rDocument ),
1360 aException
1361 ) );
1364 return bSuccess;
1367 bool MigrationEngine_Impl::impl_migrateContainerLibraries_nothrow( const SubDocument& _rDocument,
1368 const ScriptType _eScriptType, ProgressMixer& _rProgress, const PhaseID _nPhaseID ) const
1370 OSL_PRECOND( ( _eScriptType == eBasic ) || ( _eScriptType == eDialog ),
1371 "MigrationEngine_Impl::impl_migrateContainerLibraries_nothrow: illegal script type!" );
1373 bool bSuccess = false;
1374 PhaseGuard aPhase( _rProgress );
1375 Any aException;
1376 do // artificial loop for flow control only
1380 // access library container of the sub document
1381 Reference< XEmbeddedScripts > xSubDocScripts( _rDocument.xDocument, UNO_QUERY );
1382 if ( !xSubDocScripts.is() )
1383 { // no script support in the sub document -> nothing to migrate
1384 // (though ... this is suspicious, at least ...)
1385 bSuccess = true;
1386 break;
1389 Reference< XStorageBasedLibraryContainer > xSourceLibraries(
1390 _eScriptType == eBasic ? xSubDocScripts->getBasicLibraries() : xSubDocScripts->getDialogLibraries(),
1391 UNO_QUERY_THROW
1393 Reference< XLibraryContainerPassword > xSourcePasswords( xSourceLibraries, UNO_QUERY );
1394 OSL_ENSURE( xSourcePasswords.is(),
1395 "MigrationEngine_Impl::impl_migrateContainerLibraries_nothrow: suspicious: no password management for the source libraries!" );
1397 Sequence< OUString > aSourceLibNames( xSourceLibraries->getElementNames() );
1398 aPhase.start( _nPhaseID, aSourceLibNames.getLength() );
1400 if ( !xSourceLibraries->hasElements() )
1402 bSuccess = true;
1403 break;
1406 // create library containers for the document - those will be the target for the migration
1407 Reference< XStorageBasedDocument > xStorageDoc( m_xDocument, UNO_QUERY_THROW );
1408 Reference< XStorageBasedLibraryContainer > xTargetLibraries;
1409 if ( _eScriptType == eBasic )
1411 xTargetLibraries.set( DocumentScriptLibraryContainer::create(
1412 m_aContext, xStorageDoc ), UNO_QUERY_THROW );
1414 else
1416 xTargetLibraries.set( DocumentDialogLibraryContainer::create(
1417 m_aContext, xStorageDoc ), UNO_QUERY_THROW );
1420 // copy all libs to the target, with potentially renaming them
1421 const OUString* pSourceLibBegin = aSourceLibNames.getConstArray();
1422 const OUString* pSourceLibEnd = pSourceLibBegin + aSourceLibNames.getLength();
1423 for ( const OUString* pSourceLibName = pSourceLibBegin;
1424 pSourceLibName != pSourceLibEnd;
1425 ++pSourceLibName
1428 // if the library is password-protected, ask the user to unprotect it
1429 if ( xSourcePasswords.is()
1430 && xSourcePasswords->isLibraryPasswordProtected( *pSourceLibName )
1431 && !xSourcePasswords->isLibraryPasswordVerified( *pSourceLibName )
1434 if ( !impl_unprotectPasswordLibrary_throw( xSourcePasswords, _eScriptType, *pSourceLibName ) )
1436 m_rLogger.logFailure( MigrationError(
1437 ERR_PASSWORD_VERIFICATION_FAILED,
1438 _rDocument.sHierarchicalName,
1439 getScriptTypeDisplayName( _eScriptType ),
1440 *pSourceLibName
1441 ) );
1442 return false;
1446 OUString sNewLibName( lcl_createTargetLibName( _rDocument, *pSourceLibName, xTargetLibraries.get() ) );
1448 if ( xSourceLibraries->isLibraryLink( *pSourceLibName ) )
1450 // just re-create the link in the target library
1451 xTargetLibraries->createLibraryLink(
1452 sNewLibName,
1453 xSourceLibraries->getLibraryLinkURL( *pSourceLibName ),
1454 xSourceLibraries->isLibraryReadOnly( *pSourceLibName )
1457 else
1459 if ( !xSourceLibraries->isLibraryLoaded( *pSourceLibName ) )
1460 xSourceLibraries->loadLibrary( *pSourceLibName );
1462 // copy the content of this particular library
1463 Reference< XNameAccess > xSourceLib( xSourceLibraries->getByName( *pSourceLibName ), UNO_QUERY_THROW );
1464 Reference< XNameContainer > xTargetLib( xTargetLibraries->createLibrary( sNewLibName ), UNO_QUERY_THROW );
1466 Sequence< OUString > aLibElementNames( xSourceLib->getElementNames() );
1467 for ( const OUString* pSourceElementName = aLibElementNames.getConstArray();
1468 pSourceElementName != aLibElementNames.getConstArray() + aLibElementNames.getLength();
1469 ++pSourceElementName
1472 Any aElement = xSourceLib->getByName( *pSourceElementName );
1473 OSL_ENSURE( aElement.hasValue(),
1474 "MigrationEngine_Impl::impl_migrateContainerLibraries_nothrow: invalid (empty) lib element!" );
1476 // if this is a dialog, adjust the references to scripts
1477 if ( _eScriptType == eDialog )
1479 impl_adjustDialogEvents_nothrow( aElement, lcl_getSubDocumentDescription( _rDocument ),
1480 *pSourceLibName, *pSourceElementName );
1483 xTargetLib->insertByName( *pSourceElementName, aElement );
1486 // transfer the read-only flag
1487 xTargetLibraries->setLibraryReadOnly(
1488 sNewLibName, xSourceLibraries->isLibraryReadOnly( *pSourceLibName ) );
1491 // remove the source lib
1492 xSourceLibraries->removeLibrary( *pSourceLibName );
1494 // tell the logger
1495 m_rLogger.movedLibrary( m_nCurrentDocumentID, _eScriptType, *pSourceLibName, sNewLibName );
1497 // tell the progress
1498 _rProgress.advancePhase( pSourceLibName - pSourceLibBegin );
1501 // clean up
1502 xSourceLibraries->storeLibraries();
1504 xTargetLibraries->storeLibraries();
1505 Reference< XStorage > xTargetRoot( xTargetLibraries->getRootLocation(), UNO_QUERY_THROW );
1506 bSuccess = lcl_commitStorage_nothrow( xTargetRoot );
1508 catch( const Exception& )
1510 aException = ::cppu::getCaughtException();
1511 bSuccess = false;
1513 } while ( false );
1515 // log the error, if any
1516 if ( !bSuccess )
1518 m_rLogger.logFailure( MigrationError(
1519 ERR_GENERAL_MACRO_MIGRATION_FAILURE,
1520 lcl_getSubDocumentDescription( _rDocument ),
1521 aException
1522 ) );
1525 return bSuccess;
1528 bool MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow( const OUString& _rScriptType,
1529 OUString& _inout_rScriptCode ) const
1531 OSL_PRECOND( !_inout_rScriptCode.isEmpty(), "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: invalid script!" );
1532 if ( _inout_rScriptCode.isEmpty() )
1533 return false;
1535 bool bSuccess = false;
1536 Any aException;
1539 if ( _rScriptType != "Script" || _rScriptType.isEmpty() )
1541 OSL_FAIL(
1542 "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: no or unknown script type!" );
1543 m_rLogger.logRecoverable( MigrationError(
1544 ERR_UNKNOWN_SCRIPT_TYPE,
1545 _rScriptType
1546 ) );
1547 return false;
1550 // analyze the script URI
1551 Reference< XUriReferenceFactory > xUriRefFac = UriReferenceFactory::create( m_aContext );
1552 Reference< XVndSunStarScriptUrlReference > xUri( xUriRefFac->parse( _inout_rScriptCode ), UNO_QUERY_THROW );
1554 OUString sScriptLanguage = xUri->getParameter( OUString( "language" ) );
1555 ScriptType eScriptType = eBasic;
1556 if ( !lcl_getScriptTypeFromLanguage( sScriptLanguage, eScriptType ) )
1558 OSL_FAIL(
1559 "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: unknown script language!" );
1560 m_rLogger.logRecoverable( MigrationError(
1561 ERR_UNKNOWN_SCRIPT_LANGUAGE,
1562 sScriptLanguage
1563 ) );
1564 return false;
1567 OUString sLocation = xUri->getParameter( OUString( "location" ) );
1568 if ( sLocation != "document" )
1570 // only document libraries must be migrated, of course
1571 return false;
1574 OUString sScriptName = xUri->getName();
1575 sal_Int32 nLibModuleSeparator = sScriptName.indexOf( '.' );
1576 if ( nLibModuleSeparator < 0 )
1578 OSL_FAIL(
1579 "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: invalid/unknown location format!" );
1580 m_rLogger.logRecoverable( MigrationError(
1581 ERR_UNKNOWN_SCRIPT_NAME_FORMAT,
1582 sScriptName
1583 ) );
1584 return false;
1587 // replace the library name
1588 OUString sLibrary = sScriptName.copy( 0, nLibModuleSeparator );
1589 OUString sNewLibName = m_rLogger.getNewLibraryName(
1590 m_nCurrentDocumentID, eScriptType, sLibrary );
1591 OSL_ENSURE( sLibrary != sNewLibName,
1592 "MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow: a library which has not been migrated?" );
1594 xUri->setName( sNewLibName + sScriptName.copy( nLibModuleSeparator ) );
1596 // update the new script URL
1597 _inout_rScriptCode = xUri->getUriReference();
1598 bSuccess = true;
1600 catch( const Exception& )
1602 aException = ::cppu::getCaughtException();
1603 bSuccess = false;
1606 // log the failure, if any
1607 if ( !bSuccess )
1609 m_rLogger.logRecoverable( MigrationError(
1610 ERR_SCRIPT_TRANSLATION_FAILURE,
1611 _rScriptType,
1612 _inout_rScriptCode,
1613 aException
1614 ) );
1617 return bSuccess;
1620 bool MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow( ScriptEventDescriptor& _inout_rScriptEvent ) const
1622 if ( !(_inout_rScriptEvent.ScriptType.isEmpty() || _inout_rScriptEvent.ScriptCode.isEmpty()) )
1623 return impl_adjustScriptLibrary_nothrow( _inout_rScriptEvent.ScriptType, _inout_rScriptEvent.ScriptCode );
1624 return false;
1627 bool MigrationEngine_Impl::impl_adjustScriptLibrary_nothrow( Any& _inout_rScriptDescriptor ) const
1629 ::comphelper::NamedValueCollection aScriptDesc( _inout_rScriptDescriptor );
1631 OUString sScriptType;
1632 OUString sScript;
1635 OSL_VERIFY( aScriptDesc.get_ensureType( "EventType", sScriptType ) );
1636 OSL_VERIFY( aScriptDesc.get_ensureType( "Script", sScript ) );
1638 catch( const Exception& )
1640 m_rLogger.logRecoverable( MigrationError(
1641 ERR_INVALID_SCRIPT_DESCRIPTOR_FORMAT,
1642 ::cppu::getCaughtException()
1643 ) );
1646 if ( !(sScriptType.isEmpty() || sScript.isEmpty()) )
1647 if ( !impl_adjustScriptLibrary_nothrow( sScriptType, sScript ) )
1648 return false;
1650 aScriptDesc.put( "Script", sScript );
1651 _inout_rScriptDescriptor <<= aScriptDesc.getPropertyValues();
1652 return true;
1655 bool MigrationEngine_Impl::impl_adjustDocumentEvents_nothrow( const SubDocument& _rDocument ) const
1659 Reference< XEventsSupplier > xSuppEvents( _rDocument.xDocument, UNO_QUERY );
1660 if ( !xSuppEvents.is() )
1661 // this is allowed. E.g. new-style reports currently do not support this
1662 return true;
1664 Reference< XNameReplace > xEvents( xSuppEvents->getEvents(), UNO_SET_THROW );
1665 Sequence< OUString > aEventNames = xEvents->getElementNames();
1667 Any aEvent;
1668 for ( const OUString* eventName = aEventNames.getConstArray();
1669 eventName != aEventNames.getConstArray() + aEventNames.getLength();
1670 ++eventName
1673 aEvent = xEvents->getByName( *eventName );
1674 if ( !aEvent.hasValue() )
1675 continue;
1677 // translate
1678 if ( !impl_adjustScriptLibrary_nothrow( aEvent ) )
1679 continue;
1681 // put back
1682 xEvents->replaceByName( *eventName, aEvent );
1685 catch( const Exception& )
1687 m_rLogger.logRecoverable( MigrationError(
1688 ERR_ADJUSTING_DOCUMENT_EVENTS_FAILED,
1689 lcl_getSubDocumentDescription( _rDocument ),
1690 ::cppu::getCaughtException()
1691 ) );
1692 return false;
1694 return true;
1697 void MigrationEngine_Impl::impl_adjustDialogElementEvents_throw( const Reference< XInterface >& _rxElement ) const
1699 Reference< XScriptEventsSupplier > xEventsSupplier( _rxElement, UNO_QUERY_THROW );
1700 Reference< XNameReplace > xEvents( xEventsSupplier->getEvents(), UNO_QUERY_THROW );
1701 Sequence< OUString > aEventNames( xEvents->getElementNames() );
1703 const OUString* eventName = aEventNames.getArray();
1704 const OUString* eventNamesEnd = eventName + aEventNames.getLength();
1706 ScriptEventDescriptor aScriptEvent;
1707 for ( ; eventName != eventNamesEnd; ++eventName )
1709 OSL_VERIFY( xEvents->getByName( *eventName ) >>= aScriptEvent );
1711 if ( !impl_adjustScriptLibrary_nothrow( aScriptEvent ) )
1712 continue;
1714 xEvents->replaceByName( *eventName, makeAny( aScriptEvent ) );
1718 bool MigrationEngine_Impl::impl_adjustDialogEvents_nothrow( Any& _inout_rDialogLibraryElement,
1719 const OUString& _rDocName, const OUString& _rDialogLibName, const OUString& _rDialogName ) const
1723 // load a dialog model from the stream describing it
1724 Reference< XInputStreamProvider > xISP( _inout_rDialogLibraryElement, UNO_QUERY_THROW );
1725 Reference< XInputStream > xInput( xISP->createInputStream(), UNO_QUERY_THROW );
1727 Reference< XNameContainer > xDialogModel( m_aContext->getServiceManager()->createInstanceWithContext("com.sun.star.awt.UnoControlDialogModel", m_aContext), UNO_QUERY_THROW );
1728 ::xmlscript::importDialogModel( xInput, xDialogModel, m_aContext, m_xDocumentModel );
1730 // adjust the events of the dialog
1731 impl_adjustDialogElementEvents_throw( xDialogModel );
1733 // adjust the events of the controls
1734 Sequence< OUString > aControlNames( xDialogModel->getElementNames() );
1735 const OUString* controlName = aControlNames.getConstArray();
1736 const OUString* controlNamesEnd = controlName + aControlNames.getLength();
1737 for ( ; controlName != controlNamesEnd; ++controlName )
1739 impl_adjustDialogElementEvents_throw( Reference< XInterface >( xDialogModel->getByName( *controlName ), UNO_QUERY ) );
1742 // export dialog model
1743 xISP = ::xmlscript::exportDialogModel( xDialogModel, m_aContext, m_xDocumentModel );
1744 _inout_rDialogLibraryElement <<= xISP;
1746 catch( const Exception& )
1748 m_rLogger.logRecoverable( MigrationError(
1749 ERR_ADJUSTING_DIALOG_EVENTS_FAILED,
1750 _rDocName,
1751 _rDialogLibName,
1752 _rDialogName,
1753 ::cppu::getCaughtException()
1754 ) );
1755 return false;
1757 return true;
1760 void MigrationEngine_Impl::impl_adjustFormComponentEvents_throw( const Reference< XIndexAccess >& _rxComponentContainer ) const
1762 FormComponentIterator aCompIter( _rxComponentContainer );
1763 while ( aCompIter.hasMore() )
1765 // 1. adjust the component's scripts of the current component
1766 FormComponentScripts aComponent( aCompIter.next() );
1767 Sequence< ScriptEventDescriptor > aEvents( aComponent.getEvents() );
1769 bool bChangedComponentEvents = false;
1770 for ( ScriptEventDescriptor* scriptEvent = aEvents.getArray();
1771 scriptEvent != aEvents.getArray() + aEvents.getLength();
1772 ++scriptEvent
1775 if ( !impl_adjustScriptLibrary_nothrow( *scriptEvent ) )
1776 continue;
1778 bChangedComponentEvents = true;
1781 if ( bChangedComponentEvents )
1782 aComponent.setEvents( aEvents );
1784 // 2. step down if the component is a container itself
1785 Reference< XIndexAccess > xContainer( aComponent.getComponent(), UNO_QUERY );
1786 if ( xContainer.is() )
1787 impl_adjustFormComponentEvents_throw( xContainer );
1791 bool MigrationEngine_Impl::impl_adjustFormComponentEvents_nothrow( const SubDocument& _rDocument ) const
1795 DrawPageIterator aPageIter( _rDocument.xDocument );
1796 while ( aPageIter.hasMore() )
1798 Reference< XFormsSupplier > xSuppForms( aPageIter.next(), UNO_QUERY_THROW );
1799 Reference< XIndexAccess > xForms( xSuppForms->getForms(), UNO_QUERY_THROW );
1800 impl_adjustFormComponentEvents_throw( xForms );
1803 catch( const Exception& )
1805 m_rLogger.logRecoverable( MigrationError(
1806 ERR_ADJUSTING_FORMCOMP_EVENTS_FAILED,
1807 lcl_getSubDocumentDescription( _rDocument ),
1808 ::cppu::getCaughtException()
1809 ) );
1810 return false;
1812 return true;
1815 bool MigrationEngine_Impl::impl_unprotectPasswordLibrary_throw( const Reference< XLibraryContainerPassword >& _rxPasswordManager,
1816 const ScriptType _eScriptType, const OUString& _rLibraryName ) const
1818 // a human-readable description of the affected library
1819 OUString sLibraryDescription(
1820 MacroMigrationResId(STR_LIBRARY_TYPE_AND_NAME).toString().
1821 replaceFirst("$type$",
1822 getScriptTypeDisplayName(_eScriptType)).
1823 replaceFirst("$library$", _rLibraryName));
1824 //TODO: probably broken if first replaceFirst can produce
1825 // fresh instance of "$library$" in subject string of second
1826 // replaceFirst
1828 InteractionHandler aHandler( m_aContext, m_xDocumentModel );
1829 OUString sPassword;
1830 while ( true )
1832 if ( !aHandler.requestDocumentPassword( sLibraryDescription, sPassword ) )
1833 // aborted by the user
1834 return false;
1836 bool bSuccessVerification = _rxPasswordManager->verifyLibraryPassword( _rLibraryName, sPassword );
1837 if ( bSuccessVerification )
1838 return true;
1843 // MigrationEngine
1844 MigrationEngine::MigrationEngine( const Reference<XComponentContext>& _rContext,
1845 const Reference< XOfficeDatabaseDocument >& _rxDocument, IMigrationProgress& _rProgress,
1846 MigrationLog& _rLogger )
1847 :m_pImpl( new MigrationEngine_Impl( _rContext, _rxDocument, _rProgress, _rLogger ) )
1851 MigrationEngine::~MigrationEngine()
1855 sal_Int32 MigrationEngine::getFormCount() const
1857 return m_pImpl->getFormCount();
1860 sal_Int32 MigrationEngine::getReportCount() const
1862 return m_pImpl->getReportCount();
1865 bool MigrationEngine::migrateAll()
1867 return m_pImpl->migrateAll();
1870 } // namespace dbmm
1872 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */