bump product version to 5.0.4.1
[LibreOffice.git] / basic / source / basmgr / basicmanagerrepository.cxx
blobd4996f971c8930fc31162d8d2b2bccc07881f1c3
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 <basic/basicmanagerrepository.hxx>
21 #include <basic/basmgr.hxx>
22 #include "scriptcont.hxx"
23 #include "dlgcont.hxx"
24 #include <basic/sbuno.hxx>
25 #include "sbintern.hxx"
27 #include <com/sun/star/beans/XPropertySet.hpp>
28 #include <com/sun/star/document/XStorageBasedDocument.hpp>
29 #include <com/sun/star/document/XEmbeddedScripts.hpp>
30 #include <com/sun/star/frame/Desktop.hpp>
31 #include <svtools/ehdl.hxx>
32 #include <svtools/sfxecode.hxx>
33 #include <unotools/pathoptions.hxx>
34 #include <svl/smplhint.hxx>
35 #include <vcl/svapp.hxx>
36 #include <tools/debug.hxx>
37 #include <tools/diagnose_ex.h>
38 #include <tools/urlobj.hxx>
39 #include <comphelper/stl_types.hxx>
40 #include <comphelper/processfactory.hxx>
41 #include <comphelper/documentinfo.hxx>
42 #include <unotools/eventlisteneradapter.hxx>
44 #include <osl/getglobalmutex.hxx>
45 #include <rtl/instance.hxx>
46 #include <rtl/strbuf.hxx>
48 #include <map>
51 namespace basic
55 using ::com::sun::star::uno::Reference;
56 using ::com::sun::star::uno::XComponentContext;
57 using ::com::sun::star::frame::XModel;
58 using ::com::sun::star::frame::Desktop;
59 using ::com::sun::star::uno::XInterface;
60 using ::com::sun::star::uno::UNO_QUERY;
61 using ::com::sun::star::embed::XStorage;
62 using ::com::sun::star::script::XPersistentLibraryContainer;
63 using ::com::sun::star::uno::Any;
64 using ::com::sun::star::lang::XMultiServiceFactory;
65 using ::com::sun::star::uno::UNO_QUERY_THROW;
66 using ::com::sun::star::beans::XPropertySet;
67 using ::com::sun::star::uno::Exception;
68 using ::com::sun::star::document::XStorageBasedDocument;
69 using ::com::sun::star::lang::XComponent;
70 using ::com::sun::star::document::XEmbeddedScripts;
72 typedef ::std::map< Reference< XInterface >, BasicManager*, ::comphelper::OInterfaceCompare< XInterface > > BasicManagerStore;
74 typedef ::std::vector< BasicManagerCreationListener* > CreationListeners;
76 class ImplRepository : public ::utl::OEventListenerAdapter, public SfxListener
78 private:
79 friend struct CreateImplRepository;
80 ImplRepository();
82 private:
83 BasicManagerStore m_aStore;
84 CreationListeners m_aCreationListeners;
86 public:
87 static ImplRepository& Instance();
89 BasicManager* getDocumentBasicManager( const Reference< XModel >& _rxDocumentModel );
90 BasicManager* getApplicationBasicManager( bool _bCreate );
91 void setApplicationBasicManager( BasicManager* _pBasicManager );
92 void registerCreationListener( BasicManagerCreationListener& _rListener );
93 void revokeCreationListener( BasicManagerCreationListener& _rListener );
95 private:
96 /** retrieves the location at which the BasicManager for the given model
97 is stored.
99 If previously, the BasicManager for this model has never been requested,
100 then the model is added to the map, with an initial NULL BasicManager.
102 @param _rxDocumentModel
103 the model whose BasicManager's location is to be retrieved. Must not be <NULL/>.
105 @precond
106 our mutex is locked
108 BasicManager*&
109 impl_getLocationForModel( const Reference< XModel >& _rxDocumentModel );
111 /** creates a new BasicManager instance for the given model
113 @param _out_rpBasicManager
114 reference to the pointer variable that will hold the new
115 BasicManager.
117 @param _rxDocumentModel
118 the model whose BasicManager will be created. Must not be <NULL/>.
120 void impl_createManagerForModel(
121 BasicManager*& _out_rpBasicManager,
122 const Reference< XModel >& _rxDocumentModel );
124 /** creates the application-wide BasicManager
126 BasicManager* impl_createApplicationBasicManager();
128 /** notifies all listeners which expressed interest in the creation of BasicManager instances.
130 void impl_notifyCreationListeners(
131 const Reference< XModel >& _rxDocumentModel,
132 BasicManager& _rManager
135 /** retrieves the current storage of a given document
137 @param _rxDocument
138 the document whose storage is to be retrieved.
140 @param _out_rStorage
141 takes the storage upon successful return. Note that this might be <NULL/> even
142 if <TRUE/> is returned. In this case, the document has not yet been saved.
144 @return
145 <TRUE/> if the storage could be successfully retrieved (in which case
146 <arg>_out_rStorage</arg> might or might not be <NULL/>), <FALSE/> otherwise.
147 In the latter case, processing this document should stop.
149 static bool impl_getDocumentStorage_nothrow( const Reference< XModel >& _rxDocument, Reference< XStorage >& _out_rStorage );
151 /** retrieves the containers for Basic and Dialog libraries for a given document
153 @param _rxDocument
154 the document whose containers are to be retrieved.
156 @param _out_rxBasicLibraries
157 takes the basic library container upon successful return
159 @param _out_rxDialogLibraries
160 takes the dialog library container upon successful return
162 @return
163 <TRUE/> if and only if both containers exist, and could successfully be retrieved
165 static bool impl_getDocumentLibraryContainers_nothrow(
166 const Reference< XModel >& _rxDocument,
167 Reference< XPersistentLibraryContainer >& _out_rxBasicLibraries,
168 Reference< XPersistentLibraryContainer >& _out_rxDialogLibraries
171 /** initializes the given library containers, which belong to a document
173 static void impl_initDocLibraryContainers_nothrow(
174 const Reference< XPersistentLibraryContainer >& _rxBasicLibraries,
175 const Reference< XPersistentLibraryContainer >& _rxDialogLibraries
178 // OEventListenerAdapter overridables
179 virtual void _disposing( const ::com::sun::star::lang::EventObject& _rSource ) SAL_OVERRIDE;
181 // SfxListener overridables
182 virtual void Notify( SfxBroadcaster& _rBC, const SfxHint& _rHint ) SAL_OVERRIDE;
184 /** removes the Model/BasicManager pair given by iterator from our store
186 void impl_removeFromRepository( BasicManagerStore::iterator _pos );
188 private:
189 StarBASIC* impl_getDefaultAppBasicLibrary();
193 struct CreateImplRepository
195 ImplRepository* operator()()
197 static ImplRepository* pRepository = new ImplRepository;
198 return pRepository;
203 ImplRepository::ImplRepository()
208 ImplRepository& ImplRepository::Instance()
210 return *rtl_Instance< ImplRepository, CreateImplRepository, ::osl::MutexGuard, ::osl::GetGlobalMutex >::
211 create( CreateImplRepository(), ::osl::GetGlobalMutex() );
215 BasicManager* ImplRepository::getDocumentBasicManager( const Reference< XModel >& _rxDocumentModel )
217 SolarMutexGuard g;
219 /* #163556# (DR) - This function may be called recursively while
220 constructing the Basic manager and loading the Basic storage. By
221 passing the map entry received from impl_getLocationForModel() to
222 the function impl_createManagerForModel(), the new Basic manager
223 will be put immediately into the map of existing Basic managers,
224 thus a recursive call of this function will find and return it
225 without creating another instance.
227 BasicManager*& pBasicManager = impl_getLocationForModel( _rxDocumentModel );
228 if ( pBasicManager == NULL )
229 impl_createManagerForModel( pBasicManager, _rxDocumentModel );
231 return pBasicManager;
235 BasicManager* ImplRepository::getApplicationBasicManager( bool _bCreate )
237 SolarMutexGuard g;
239 BasicManager* pAppManager = GetSbData()->pAppBasMgr;
240 if ( ( pAppManager == NULL ) && _bCreate )
241 pAppManager = impl_createApplicationBasicManager();
243 return pAppManager;
247 void ImplRepository::setApplicationBasicManager( BasicManager* _pBasicManager )
249 SolarMutexGuard g;
251 BasicManager* pPreviousManager = getApplicationBasicManager( false );
252 delete pPreviousManager;
254 GetSbData()->pAppBasMgr = _pBasicManager;
258 BasicManager* ImplRepository::impl_createApplicationBasicManager()
260 SolarMutexGuard g;
262 OSL_PRECOND( getApplicationBasicManager( false ) == NULL, "ImplRepository::impl_createApplicationBasicManager: there already is one!" );
264 // Determine Directory
265 SvtPathOptions aPathCFG;
266 OUString aAppBasicDir( aPathCFG.GetBasicPath() );
267 if ( aAppBasicDir.isEmpty() )
269 aPathCFG.SetBasicPath(OUString("$(prog)"));
272 // Create basic and load it
273 // AppBasicDir is now a PATH
274 INetURLObject aAppBasic( SvtPathOptions().SubstituteVariable(OUString("$(progurl)")) );
275 aAppBasic.insertName( Application::GetAppName() );
277 BasicManager* pBasicManager = new BasicManager( new StarBASIC, &aAppBasicDir );
278 setApplicationBasicManager( pBasicManager );
280 // The first dir in the path as destination:
281 OUString aFileName( aAppBasic.getName() );
282 aAppBasic = INetURLObject( aAppBasicDir.getToken(1, ';') );
283 DBG_ASSERT(aAppBasic.GetProtocol() != INetProtocol::NotValid,
284 OString("Invalid URL: \"" +
285 OUStringToOString(aAppBasicDir, osl_getThreadTextEncoding()) +
286 "\"").getStr());
287 aAppBasic.insertName( aFileName );
288 pBasicManager->SetStorageName( aAppBasic.PathToFileName() );
290 // Basic container
291 SfxScriptLibraryContainer* pBasicCont = new SfxScriptLibraryContainer( Reference< XStorage >() );
292 Reference< XPersistentLibraryContainer > xBasicCont( pBasicCont );
293 pBasicCont->setBasicManager( pBasicManager );
295 // Dialog container
296 SfxDialogLibraryContainer* pDialogCont = new SfxDialogLibraryContainer( Reference< XStorage >() );
297 Reference< XPersistentLibraryContainer > xDialogCont( pDialogCont );
299 LibraryContainerInfo aInfo( xBasicCont, xDialogCont, static_cast< OldBasicPassword* >( pBasicCont ) );
300 pBasicManager->SetLibraryContainerInfo( aInfo );
302 // global constants
304 // StarDesktop
305 Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
306 pBasicManager->SetGlobalUNOConstant( "StarDesktop", makeAny( Desktop::create(xContext)));
308 // (BasicLibraries and DialogLibraries have automatically been added in SetLibraryContainerInfo)
310 // notify
311 impl_notifyCreationListeners( NULL, *pBasicManager );
313 // outta here
314 return pBasicManager;
318 void ImplRepository::registerCreationListener( BasicManagerCreationListener& _rListener )
320 SolarMutexGuard g;
322 m_aCreationListeners.push_back( &_rListener );
326 void ImplRepository::revokeCreationListener( BasicManagerCreationListener& _rListener )
328 SolarMutexGuard g;
330 CreationListeners::iterator pos = ::std::find( m_aCreationListeners.begin(), m_aCreationListeners.end(), &_rListener );
331 if ( pos != m_aCreationListeners.end() )
332 m_aCreationListeners.erase( pos );
333 else {
334 OSL_FAIL( "ImplRepository::revokeCreationListener: listener is not registered!" );
339 void ImplRepository::impl_notifyCreationListeners( const Reference< XModel >& _rxDocumentModel, BasicManager& _rManager )
341 for ( CreationListeners::const_iterator loop = m_aCreationListeners.begin();
342 loop != m_aCreationListeners.end();
343 ++loop
346 (*loop)->onBasicManagerCreated( _rxDocumentModel, _rManager );
351 StarBASIC* ImplRepository::impl_getDefaultAppBasicLibrary()
353 BasicManager* pAppManager = getApplicationBasicManager( true );
355 StarBASIC* pAppBasic = pAppManager ? pAppManager->GetLib(0) : NULL;
356 DBG_ASSERT( pAppBasic != NULL, "impl_getApplicationBasic: unable to determine the default application's Basic library!" );
357 return pAppBasic;
361 BasicManager*& ImplRepository::impl_getLocationForModel( const Reference< XModel >& _rxDocumentModel )
363 Reference< XInterface > xNormalized( _rxDocumentModel, UNO_QUERY );
364 DBG_ASSERT( _rxDocumentModel.is(), "ImplRepository::impl_getLocationForModel: invalid model!" );
366 BasicManager*& location = m_aStore[ xNormalized ];
367 return location;
371 void ImplRepository::impl_initDocLibraryContainers_nothrow( const Reference< XPersistentLibraryContainer >& _rxBasicLibraries, const Reference< XPersistentLibraryContainer >& _rxDialogLibraries )
373 OSL_PRECOND( _rxBasicLibraries.is() && _rxDialogLibraries.is(),
374 "ImplRepository::impl_initDocLibraryContainers_nothrow: illegal library containers, this will crash!" );
378 // ensure there's a standard library in the basic container
379 OUString aStdLibName( "Standard" );
380 if ( !_rxBasicLibraries->hasByName( aStdLibName ) )
382 _rxBasicLibraries->createLibrary( aStdLibName );
384 // as well as in the dialog container
385 if ( !_rxDialogLibraries->hasByName( aStdLibName ) )
387 _rxDialogLibraries->createLibrary( aStdLibName );
390 catch( const Exception& )
392 DBG_UNHANDLED_EXCEPTION();
397 void ImplRepository::impl_createManagerForModel( BasicManager*& _out_rpBasicManager, const Reference< XModel >& _rxDocumentModel )
399 StarBASIC* pAppBasic = impl_getDefaultAppBasicLibrary();
401 _out_rpBasicManager = 0;
402 Reference< XStorage > xStorage;
403 if ( !impl_getDocumentStorage_nothrow( _rxDocumentModel, xStorage ) )
405 // the document is not able to provide the storage it is based on.
406 return;
408 Reference< XPersistentLibraryContainer > xBasicLibs;
409 Reference< XPersistentLibraryContainer > xDialogLibs;
410 if ( !impl_getDocumentLibraryContainers_nothrow( _rxDocumentModel, xBasicLibs, xDialogLibs ) )
411 // the document does not have BasicLibraries and DialogLibraries
412 return;
414 if ( xStorage.is() )
416 // load BASIC-manager
417 SfxErrorContext aErrContext( ERRCTX_SFX_LOADBASIC,
418 ::comphelper::DocumentInfo::getDocumentTitle( _rxDocumentModel ) );
419 OUString aAppBasicDir = SvtPathOptions().GetBasicPath();
421 // Storage and BaseURL are only needed by binary documents!
422 tools::SvRef<SotStorage> xDummyStor = new SotStorage( OUString() );
423 _out_rpBasicManager = new BasicManager( *xDummyStor, OUString() /* TODO/LATER: xStorage */,
424 pAppBasic,
425 &aAppBasicDir, true );
426 if ( !_out_rpBasicManager->GetErrors().empty() )
428 // handle errors
429 std::vector<BasicError>& aErrors = _out_rpBasicManager->GetErrors();
430 for(std::vector<BasicError>::const_iterator i = aErrors.begin(); i != aErrors.end(); ++i)
432 // show message to user
433 if ( ERRCODE_BUTTON_CANCEL == ErrorHandler::HandleError( i->GetErrorId() ) )
435 // user wants to break loading of BASIC-manager
436 delete _out_rpBasicManager;
437 _out_rpBasicManager = NULL;
438 xStorage.clear();
439 break;
445 // not loaded?
446 if ( !xStorage.is() )
448 // create new BASIC-manager
449 StarBASIC* pBasic = new StarBASIC( pAppBasic );
450 pBasic->SetFlag( SBX_EXTSEARCH );
451 _out_rpBasicManager = new BasicManager( pBasic, NULL, true );
454 // knit the containers with the BasicManager
455 LibraryContainerInfo aInfo( xBasicLibs, xDialogLibs, dynamic_cast< OldBasicPassword* >( xBasicLibs.get() ) );
456 OSL_ENSURE( aInfo.mpOldBasicPassword, "ImplRepository::impl_createManagerForModel: wrong BasicLibraries implementation!" );
457 _out_rpBasicManager->SetLibraryContainerInfo( aInfo );
459 // initialize the containers
460 impl_initDocLibraryContainers_nothrow( xBasicLibs, xDialogLibs );
462 // so that also dialogs etc. could be 'qualified' addressed
463 _out_rpBasicManager->GetLib(0)->SetParent( pAppBasic );
465 // global properties in the document's Basic
466 _out_rpBasicManager->SetGlobalUNOConstant( "ThisComponent", makeAny( _rxDocumentModel ) );
468 // notify
469 impl_notifyCreationListeners( _rxDocumentModel, *_out_rpBasicManager );
471 // register as listener for this model being disposed/closed
472 OSL_ENSURE( _rxDocumentModel.is(), "ImplRepository::impl_createManagerForModel: the document must be an XComponent!" );
473 startComponentListening( _rxDocumentModel );
475 // register as listener for the BasicManager being destroyed
476 StartListening( *_out_rpBasicManager );
478 // #i104876: Library container must not be modified just after
479 // creation. This happens as side effect when creating default
480 // "Standard" libraries and needs to be corrected here
481 xBasicLibs->setModified( sal_False );
482 xDialogLibs->setModified( sal_False );
487 bool ImplRepository::impl_getDocumentStorage_nothrow( const Reference< XModel >& _rxDocument, Reference< XStorage >& _out_rStorage )
489 _out_rStorage.clear();
492 Reference< XStorageBasedDocument > xStorDoc( _rxDocument, UNO_QUERY_THROW );
493 _out_rStorage.set( xStorDoc->getDocumentStorage() );
495 catch( const Exception& )
497 DBG_UNHANDLED_EXCEPTION();
498 return false;
500 return true;
504 bool ImplRepository::impl_getDocumentLibraryContainers_nothrow( const Reference< XModel >& _rxDocument,
505 Reference< XPersistentLibraryContainer >& _out_rxBasicLibraries, Reference< XPersistentLibraryContainer >& _out_rxDialogLibraries )
507 _out_rxBasicLibraries.clear();
508 _out_rxDialogLibraries.clear();
511 Reference< XEmbeddedScripts > xScripts( _rxDocument, UNO_QUERY_THROW );
512 _out_rxBasicLibraries.set( xScripts->getBasicLibraries(), UNO_QUERY_THROW );
513 _out_rxDialogLibraries.set( xScripts->getDialogLibraries(), UNO_QUERY_THROW );
515 catch( const Exception& )
517 DBG_UNHANDLED_EXCEPTION();
519 return _out_rxBasicLibraries.is() && _out_rxDialogLibraries.is();
523 void ImplRepository::impl_removeFromRepository( BasicManagerStore::iterator _pos )
525 OSL_PRECOND( _pos != m_aStore.end(), "ImplRepository::impl_removeFromRepository: invalid position!" );
527 BasicManager* pManager = _pos->second;
529 // *first* remove from map (else Notify won't work properly)
530 m_aStore.erase( _pos );
532 // *then* delete the BasicManager
533 EndListening( *pManager );
534 delete pManager;
538 void ImplRepository::_disposing( const ::com::sun::star::lang::EventObject& _rSource )
540 SolarMutexGuard g;
542 Reference< XInterface > xNormalizedSource( _rSource.Source, UNO_QUERY );
543 #if OSL_DEBUG_LEVEL > 0
544 bool bFound = false;
545 #endif
547 for ( BasicManagerStore::iterator loop = m_aStore.begin();
548 loop != m_aStore.end();
549 ++loop
552 if ( loop->first.get() == xNormalizedSource.get() )
554 impl_removeFromRepository( loop );
555 #if OSL_DEBUG_LEVEL > 0
556 bFound = true;
557 #endif
558 break;
562 OSL_ENSURE( bFound, "ImplRepository::_disposing: where does this come from?" );
566 void ImplRepository::Notify( SfxBroadcaster& _rBC, const SfxHint& _rHint )
568 const SfxSimpleHint* pSimpleHint = dynamic_cast< const SfxSimpleHint* >( &_rHint );
569 if ( !pSimpleHint || ( pSimpleHint->GetId() != SFX_HINT_DYING ) )
570 // not interested in
571 return;
573 BasicManager* pManager = dynamic_cast< BasicManager* >( &_rBC );
574 OSL_ENSURE( pManager, "ImplRepository::Notify: where does this come from?" );
576 for ( BasicManagerStore::iterator loop = m_aStore.begin();
577 loop != m_aStore.end();
578 ++loop
581 if ( loop->second == pManager )
583 // a BasicManager which is still in our repository is being deleted.
584 // That's bad, since by definition, we *own* all instances in our
585 // repository.
586 OSL_FAIL( "ImplRepository::Notify: nobody should tamper with the managers, except ourself!" );
587 m_aStore.erase( loop );
588 break;
594 BasicManager* BasicManagerRepository::getDocumentBasicManager( const Reference< XModel >& _rxDocumentModel )
596 return ImplRepository::Instance().getDocumentBasicManager( _rxDocumentModel );
600 BasicManager* BasicManagerRepository::getApplicationBasicManager( bool _bCreate )
602 return ImplRepository::Instance().getApplicationBasicManager( _bCreate );
606 void BasicManagerRepository::resetApplicationBasicManager()
608 return ImplRepository::Instance().setApplicationBasicManager( NULL );
612 void BasicManagerRepository::registerCreationListener( BasicManagerCreationListener& _rListener )
614 ImplRepository::Instance().registerCreationListener( _rListener );
618 void BasicManagerRepository::revokeCreationListener( BasicManagerCreationListener& _rListener )
620 ImplRepository::Instance().revokeCreationListener( _rListener );
624 } // namespace basic
627 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */